diff --git a/Urban_Footprint_Sphinx_Docs/Makefile b/Urban_Footprint_Sphinx_Docs/Makefile deleted file mode 100644 index 1f64b79c2..000000000 --- a/Urban_Footprint_Sphinx_Docs/Makefile +++ /dev/null @@ -1,153 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/UrbanFootprint.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/UrbanFootprint.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/UrbanFootprint" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/UrbanFootprint" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/Urban_Footprint_Sphinx_Docs/UrbanFootprint Technical Summary Text- 9 November 2012.docx b/Urban_Footprint_Sphinx_Docs/UrbanFootprint Technical Summary Text- 9 November 2012.docx deleted file mode 100644 index 6eab40551..000000000 Binary files a/Urban_Footprint_Sphinx_Docs/UrbanFootprint Technical Summary Text- 9 November 2012.docx and /dev/null differ diff --git a/Urban_Footprint_Sphinx_Docs/_static/basic_screenshot.png b/Urban_Footprint_Sphinx_Docs/_static/basic_screenshot.png deleted file mode 100644 index 567dbe376..000000000 Binary files a/Urban_Footprint_Sphinx_Docs/_static/basic_screenshot.png and /dev/null differ diff --git a/Urban_Footprint_Sphinx_Docs/_static/default.css b/Urban_Footprint_Sphinx_Docs/_static/default.css deleted file mode 100644 index 9a3dcf929..000000000 --- a/Urban_Footprint_Sphinx_Docs/_static/default.css +++ /dev/null @@ -1,501 +0,0 @@ -/** - * Alternate Sphinx design - * Originally created by Armin Ronacher for Werkzeug, adapted by Georg Brandl. - */ - -body { - font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif; - font-size: 14px; - letter-spacing: -0.01em; - line-height: 150%; - text-align: center; - /*background-color: #AFC1C4; */ - background-color: #BFD1D4; - color: black; - padding: 0; - border: 1px solid #aaa; - - margin: 0px 80px 0px 80px; - min-width: 740px; -} - -a { - color: #CA7900; - text-decoration: none; -} - -a:hover { - color: #2491CF; -} - -pre { - font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.95em; - letter-spacing: 0.015em; - padding: 0.5em; - border: 1px solid #ccc; - background-color: #f8f8f8; -} - -td.linenos pre { - padding: 0.5em 0; - border: 0; - background-color: transparent; - color: #aaa; -} - -table.highlighttable { - margin-left: 0.5em; -} - -table.highlighttable td { - padding: 0 0.5em 0 0.5em; -} - -cite, code, tt { - font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; - font-size: 0.95em; - letter-spacing: 0.01em; -} - -hr { - border: 1px solid #abc; - margin: 2em; -} - -tt.descname { - background-color: transparent; - font-weight: bold; - font-size: 1.2em; - border: 0; -} - -tt.descclassname { - background-color: transparent; - border: 0; -} - -tt.xref { - background-color: transparent; - font-weight: bold; - border: 0; -} - -a tt { - background-color: transparent; - font-weight: bold; - border: 0; - color: #CA7900; -} - -a tt:hover { - color: #2491CF; -} - -dl { - margin-bottom: 15px; -} - -dd p { - margin-top: 0px; -} - -dd ul, dd table { - margin-bottom: 10px; -} - -dd { - margin-top: 3px; - margin-bottom: 10px; - margin-left: 30px; -} - -.refcount { - color: #060; -} - -dt:target, -.highlight { - background-color: #fbe54e; -} - -dl.class, dl.function { - border-top: 2px solid #888; -} - -dl.method, dl.attribute { - border-top: 1px solid #aaa; -} - -dl.glossary dt { - font-weight: bold; - font-size: 1.1em; -} - -pre { - line-height: 120%; -} - -pre a { - color: inherit; - text-decoration: underline; -} - -.first { - margin-top: 0 !important; -} - -div.document { - background-color: white; - text-align: left; - background-image: url(contents.png); - background-repeat: repeat-x; -} - -/* -div.documentwrapper { - width: 100%; -} -*/ - -div.clearer { - clear: both; -} - -div.related h3 { - display: none; -} - -div.related ul { - background-image: url(navigation.png); - height: 2em; - list-style: none; - border-top: 1px solid #ddd; - border-bottom: 1px solid #ddd; - margin: 0; - padding-left: 10px; -} - -div.related ul li { - margin: 0; - padding: 0; - height: 2em; - float: left; -} - -div.related ul li.right { - float: right; - margin-right: 5px; -} - -div.related ul li a { - margin: 0; - padding: 0 5px 0 5px; - line-height: 1.75em; - color: #EE9816; -} - -div.related ul li a:hover { - color: #3CA8E7; -} - -div.body { - margin: 0; - padding: 0.5em 20px 20px 20px; -} - -div.bodywrapper { - margin: 0 240px 0 0; - border-right: 1px solid #ccc; -} - -div.body a { - text-decoration: underline; -} - -div.sphinxsidebar { - margin: 0; - padding: 0.5em 15px 15px 0; - width: 210px; - float: right; - text-align: left; -/* margin-left: -100%; */ -} - -div.sphinxsidebar h4, div.sphinxsidebar h3 { - margin: 1em 0 0.5em 0; - font-size: 0.9em; - padding: 0.1em 0 0.1em 0.5em; - color: white; - border: 1px solid #86989B; - background-color: #AFC1C4; -} - -div.sphinxsidebar ul { - padding-left: 1.5em; - margin-top: 7px; - list-style: none; - padding: 0; - line-height: 130%; -} - -div.sphinxsidebar ul ul { - list-style: square; - margin-left: 20px; -} - -p { - margin: 0.8em 0 0.5em 0; -} - -p.rubric { - font-weight: bold; -} - -h1 { - margin: 0; - padding: 0.7em 0 0.3em 0; - font-size: 1.5em; - color: #11557C; -} - -h2 { - margin: 1.3em 0 0.2em 0; - font-size: 1.35em; - padding: 0; -} - -h3 { - margin: 1em 0 -0.3em 0; - font-size: 1.2em; -} - -h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { - color: black!important; -} - -h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { - display: none; - margin: 0 0 0 0.3em; - padding: 0 0.2em 0 0.2em; - color: #aaa!important; -} - -h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, -h5:hover a.anchor, h6:hover a.anchor { - display: inline; -} - -h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, -h5 a.anchor:hover, h6 a.anchor:hover { - color: #777; - background-color: #eee; -} - -table { - border-collapse: collapse; - margin: 0 -0.5em 0 -0.5em; -} - -table td, table th { - padding: 0.2em 0.5em 0.2em 0.5em; -} - -div.footer { - background-color: #E3EFF1; - color: #86989B; - padding: 3px 8px 3px 0; - clear: both; - font-size: 0.8em; - text-align: right; -} - -div.footer a { - color: #86989B; - text-decoration: underline; -} - -div.pagination { - margin-top: 2em; - padding-top: 0.5em; - border-top: 1px solid black; - text-align: center; -} - -div.sphinxsidebar ul.toc { - margin: 1em 0 1em 0; - padding: 0 0 0 0.5em; - list-style: none; -} - -div.sphinxsidebar ul.toc li { - margin: 0.5em 0 0.5em 0; - font-size: 0.9em; - line-height: 130%; -} - -div.sphinxsidebar ul.toc li p { - margin: 0; - padding: 0; -} - -div.sphinxsidebar ul.toc ul { - margin: 0.2em 0 0.2em 0; - padding: 0 0 0 1.8em; -} - -div.sphinxsidebar ul.toc ul li { - padding: 0; -} - -div.admonition, div.warning { - font-size: 0.9em; - margin: 1em 0 0 0; - border: 1px solid #86989B; - background-color: #f7f7f7; -} - -div.admonition p, div.warning p { - margin: 0.5em 1em 0.5em 1em; - padding: 0; -} - -div.admonition pre, div.warning pre { - margin: 0.4em 1em 0.4em 1em; -} - -div.admonition p.admonition-title, -div.warning p.admonition-title { - margin: 0; - padding: 0.1em 0 0.1em 0.5em; - color: white; - border-bottom: 1px solid #86989B; - font-weight: bold; - background-color: #AFC1C4; -} - -div.warning { - border: 1px solid #940000; -} - -div.warning p.admonition-title { - background-color: #CF0000; - border-bottom-color: #940000; -} - -div.admonition ul, div.admonition ol, -div.warning ul, div.warning ol { - margin: 0.1em 0.5em 0.5em 3em; - padding: 0; -} - -div.versioninfo { - margin: 1em 0 0 0; - border: 1px solid #ccc; - background-color: #DDEAF0; - padding: 8px; - line-height: 1.3em; - font-size: 0.9em; -} - - -a.headerlink { - color: #c60f0f!important; - font-size: 1em; - margin-left: 6px; - padding: 0 4px 0 4px; - text-decoration: none!important; - visibility: hidden; -} - -h1:hover > a.headerlink, -h2:hover > a.headerlink, -h3:hover > a.headerlink, -h4:hover > a.headerlink, -h5:hover > a.headerlink, -h6:hover > a.headerlink, -dt:hover > a.headerlink { - visibility: visible; -} - -a.headerlink:hover { - background-color: #ccc; - color: white!important; -} - -table.indextable td { - text-align: left; - vertical-align: top; -} - -table.indextable dl, table.indextable dd { - margin-top: 0; - margin-bottom: 0; -} - -table.indextable tr.pcap { - height: 10px; -} - -table.indextable tr.cap { - margin-top: 10px; - background-color: #f2f2f2; -} - -img.toggler { - margin-right: 3px; - margin-top: 3px; - cursor: pointer; -} - -img.inheritance { - border: 0px -} - -form.pfform { - margin: 10px 0 20px 0; -} - -table.contentstable { - width: 90%; -} - -table.contentstable p.biglink { - line-height: 150%; -} - -a.biglink { - font-size: 1.3em; -} - -span.linkdescr { - font-style: italic; - padding-top: 5px; - font-size: 90%; -} - -ul.search { - margin: 10px 0 0 20px; - padding: 0; -} - -ul.search li { - padding: 5px 0 5px 20px; - background-image: url(file.png); - background-repeat: no-repeat; - background-position: 0 7px; -} - -ul.search li a { - font-weight: bold; -} - -ul.search li div.context { - color: #888; - margin: 2px 0 0 30px; - text-align: left; -} - -ul.keywordmatches li.goodmatch a { - font-weight: bold; -} diff --git a/Urban_Footprint_Sphinx_Docs/_static/logo.png b/Urban_Footprint_Sphinx_Docs/_static/logo.png deleted file mode 100644 index 497745cd0..000000000 Binary files a/Urban_Footprint_Sphinx_Docs/_static/logo.png and /dev/null differ diff --git a/Urban_Footprint_Sphinx_Docs/_templates/layout.html b/Urban_Footprint_Sphinx_Docs/_templates/layout.html deleted file mode 100644 index 5213a4852..000000000 --- a/Urban_Footprint_Sphinx_Docs/_templates/layout.html +++ /dev/null @@ -1,25 +0,0 @@ - -{% extends "!layout.html" %} - - -{% block rootrellink %} -
  • home
  • -
  • search
  • -
  • documentation »
  • -{% endblock %} - - -{% block relbar1 %} - -
    -sampledoc -
    -{{ super() }} -{% endblock %} - -{# put the sidebar before the body #} -{% block sidebar1 %}{{ sidebar() }}{% endblock %} -{% block sidebar2 %}{% endblock %} - - diff --git a/Urban_Footprint_Sphinx_Docs/building_definition.rst b/Urban_Footprint_Sphinx_Docs/building_definition.rst deleted file mode 100644 index 7f4ce1b99..000000000 --- a/Urban_Footprint_Sphinx_Docs/building_definition.rst +++ /dev/null @@ -1,117 +0,0 @@ -Building Definition -************************** - -=========================================================== ============================================================================================================================================================================================================================================================================================================================================================= ================================================================================================================================================================================== -Field Name Definition Equation -=========================================================== ============================================================================================================================================================================================================================================================================================================================================================= ================================================================================================================================================================================== -record_id This is the auto-generated unique identifier for each record, allowing for joins to other tables. -classtype For buildings and building types, refers to the overall class of buildings, i.e. Mixed Use, Residential, Employment, Civic, or Agricultural. -name Name of the building, building type or place type (text). -pt_num Place type number (text), sometimes used as a unique ID when referring to place types. -direction Place type compass rose direction (NW, NE, SE, SW or C), used when referring to compass rose place types. -ptid Unique identifier, combination of pt_num + direction. -place_type Name of the place type. -intersections_sqmi Intersections per square mile for the place type. -gross_net_ratio Gross to net ratio for the place type; it represents the amount of gross land that is developable on the net. *e.g.* A gross-to-net ratio of 0.55 means that 55% of the gross land is available for development, generally in the form of developable parcel area. -pop_density Population density is calculated as a product of household size and household density. pop_density = hh_average_size * hh_density -du_density For multifamily and townhome buildings du_density is calculated based on total residential built up area and the average square feet per dwelling unit. For single family homes, it is based on the size of the lot. DU_density = residential_built_up_area_square_feet_net / square_feet_per_du_average DU_density = square_feet_per_acre / lot_size_average -du_use_density Density of dwelling units on residential parcel acres is derived as total dwelling units over parcel acres containing a residential use. DU_use_density = DU_density / residential_use_acres -building_percent_residential Percent of each building that is residential. This is an input that is used to define the characteristics of each building. -building_percent_retail Percent of each building that is retail employment. This is an input that is used to define the characteristics of each building. -building_percent_office Percent of each building that is office employment. This is an input that is used to define the characteristics of each building. -building_percent_industrial Percent of each building that is industrial employment. This is an input that is used to define the characteristics of each building. -building_percent_agriculture Percent of each building that is agricultural employment. This is an input that is used to define the characteristics of each building. -building_percent_armed_forces Percent of each building that is armed forces employment. This is an input that is used to define the characteristics of each building. -floor_area_ratio The Floor Area Ratio (FAR) is an input used to define the characteristics of each building. When calculating its characteristics for place types it is derived as total building square feet of gross building built up area (gross BUA) over total parcel square feet, producing net FAR (Gross FAR uses total site area as the denominator). floor_area_ratio = building_square_feet_total_gross / parcel_square_feet -floor_area_ratio_residential The residential Floor Area Ratio (FAR) is derived differently depending on the building type. For multifamily and townhome, the calculation is based on total FAR and the residential building percent. For single family homes, the calculation involves the average single family lot size and the average square feet per dwelling unit. floor_area_ratio_residential = building_percent_residential * floor_area_ratio floor_area_ratio_residential = square_feet_per_du_average / lot_size_average -floor_area_ratio_retail The retail Floor Area Ratio (FAR) calculation is based on total FAR and the retail building percent. floor_area_ratio_retail = building_percent_retail * floor_area_ratio -floor_area_ratio_office The office Floor Area Ratio (FAR) calculation is based on total FAR and the office building percent. floor_area_ratio_office = building_percent_office * floor_area_ratio -floor_area_ratio_industrial The industrial Floor Area Ratio (FAR) calculation is based on total FAR and the industrial building percent. floor_area_ratio_industrial = building_percent_industrial * floor_area_ratio -floor_area_ratio_agriculture The agriculture Floor Area Ratio (FAR) calculation is based on total FAR and the agriculture building percent. floor_area_ratio_agriculture = building_percent_agriculture * floor_area_ratio -floor_area_ratio_armed_forces The armed forces Floor Area Ratio (FAR) calculation is based on total FAR and the armed forces building percent. floor_area_ratio_armed_forces = building_percent_armed_forces * floor_area_ratio -residential_use_acres Residential use acres are defined as parcel acres containing residential use. -detached_single_family_large_lot_density Density of single family large lot dwelling units is derived as total single family large lot dwelling units over total parcel acres. detached_single_family_large_lot_density = detached_single_family_large_lot_units / parcel_acres -detached_single_family_small_lot_density Density of single family small lot dwelling units is derived as total single family small lot dwelling units over total parcel acres. detached_single_family_small_lot_density = detached_single_family_small_lot_units / parcel_acres -attached_single_family_density Density of single family attached (town home) dwelling units is derived as total single family attached dwelling units over total parcel acres. attached_single_family_density = attached_single_family_units / parcel_acres -multifamily_density Density of multifamily dwelling units on total parcel acres multifamily_density = multifamily_units / parcel_acres -hh_density Density of households is derived from total dwelling units based on the vacancy rate. hh_density = DU_density * (1 - residential_vacancy_rate) -hh1_percent Percent of all households with one person. This is an input that is used to define the distribution of households by size. -hh2_percent Percent of all households with two people. This is an input that is used to define the distribution of households by size. -hh3_percent Percent of all households with three people. This is an input that is used to define the distribution of households by size. -hh4_percent Percent of all households with four people. This is an input that is used to define the distribution of households by size. -hh5p_percent Percent of all households with five or more people. This is an input that is used to define the distribution of households by size. -hh1 Number of households with one person. This is derived from the percent of all households with one person and the total number of households. hh1 = hh1_percent * hh_density -hh2 Number of households with one person. This is derived from the percent of all households with one person and the total number of households. hh2 = hh2_percent * hh_density -hh3 Number of households with one person. This is derived from the percent of all households with one person and the total number of households. hh3 = hh3_percent * hh_density -hh4 Number of households with one person. This is derived from the percent of all households with one person and the total number of households. hh4 = hh4_percent * hh_density -hh5p Number of households with one person. This is derived from the percent of all households with one person and the total number of households. hh5 = hh5_percent * hh_density -hh_average_size Average household size. This is derived from the number of households by household size category and the total number of households. hh_average_size = ((hh1 * 1) + (hh2 * 2) + (hh3 * 3) + (hh4 * 4) + (hh5 * 5)) / hh_density -residential_vacancy_rate Unoccupied percentage of total dwelling units (inverse of occupancy rate); building-level input. -square_feet_per_du_average Average square feet per residential dwelling unit. This is an input that is used to calculate residential density, among other things. -residential_built_up_area_square_feet_gross Total square feet of building associated with residential uses, including common areas. This is derived from the residential Floor Area Ratio (FAR). residential_built_up_area_square_feet_gross = floor_area_ratio_residential * square_feet_per_acre -residential_percent_efficiency The percent of residential_built_up_area_square_feet_gross that is leasable to tenants; the inverse of which represents residential common areas. -residential_built_up_area_square_feet_net The leasable portion of a residential building; that is, the amount contained within dwelling units, excluding indoor residential common areas. residential_built_up_area_square_feet_net = residential_built_up_area_square_feet_gross * residential_percent_efficiency -emp_density The density of total employees (workers at work) per acre. This is calculated by summing the densities of each of the employment types. emp_density = emp_retail_density + emp_office_density + emp_industrial_density + emp_agriculture_density + emp_armed_forces_density -emp_use_density Density of employees (workers at work) on employment parcel acres is derived as total employees over parcel acres containing an employment use. EMP_use_density = EMP_density / employment_use_acres -employment_use_acres Employment use acres are defined as parcel acres containing an employment use. -emp_retail_density The density of total retail employees (workers at work) per acre. This is calculated based on the net retail building square feet and the square feet per retail employee. emp_retail_density = building_square_feet_retail_net / square_feet_per_retail_employee_average -emp_office_density The density of total office employees (workers at work) per acre. This is calculated based on the net office building square feet and the square feet per office employee. emp_office_density = building_square_feet_office_net / square_feet_per_office_employee_average -emp_industrial_density The density of total industrial employees (workers at work) per acre. This is calculated based on the net industrial building square feet and the square feet per industrial employee. emp_industrial_density = building_square_feet_industrial_net / square_feet_per_industrial_employee_average -emp_agriculture_density The density of total agricultural employees (workers at work) per acre. This is calculated based on the net agricultural building square feet and the square feet per agricultural employee. emp_agriculture_density = building_square_feet_agriculture_net / square_feet_per_agricultural_employee_average -emp_armed_forces_density The density of total armed forces employees (workers at work) per acre. This is calculated based on the net armed forces building square feet and the square feet per armed forces employee. emp_armed_forces_density = building_square_feet_armed_forces_net / _feet_per_armed_forces_employee_average -square_feet_per_retail_employee_average The amount of net building square feet per retail employee; this is a building-level input. -square_feet_per_office_employee_average The amount of net building square feet per office employee; this is a building-level input. -square_feet_per_industrial_employee_average The amount of net building square feet per industrial employee; this is a building-level input. -square_feet_per_agricultural_employee_average The amount of net building square feet per agricultural employee; this is a building-level input. -square_feet_per_armed_forces_employee_average The amount of net building square feet per armed forces employee; this is a building-level input. -retail_percent_efficiency The percent of building_square_feet_retail_gross that is leasable to tenants; the inverse of which represents building common areas. This is a building-level input. -office_percent_efficiency The percent of building_square_feet_office_gross that is leasable to tenants; the inverse of which represents building common areas. This is a building-level input. -industrial_percent_efficiency The percent of building_square_feet_industrial_gross that is leasable to tenants; the inverse of which represents building common areas. This is a building-level input. -agriculture_percent_efficiency The percent of building_square_feet_agriculture_gross that is leasable to tenants; the inverse of which represents building common areas. This is a building-level input. -armed_forces_percent_efficiency The percent of building_square_feet_armed_forces_gross that is leasable to tenants; the inverse of which represents building common areas. This is a building-level input. -building_square_feet_total_gross -building_square_feet_total_net -building_square_feet_retail_gross Total square feet of building associated with retail uses, including common areas. This is derived from the retail Floor Area Ratio (FAR). building_square_feet_retail_gross = floor_area_ratio_retail * square_feet_per_acre -building_square_feet_office_gross Total square feet of building associated with office uses, including common areas. This is derived from the office Floor Area Ratio (FAR). building_square_feet_office_gross = floor_area_ratio_office * square_feet_per_acre -building_square_feet_industrial_gross Total square feet of building associated with industrial uses, including common areas. This is derived from the industrial Floor Area Ratio (FAR). building_square_feet_industrial_gross = floor_area_ratio_industrial * square_feet_per_acre -building_square_feet_agriculture_gross Total square feet of building associated with agricultural uses, including common areas. This is derived from the agricultural Floor Area Ratio (FAR). building_square_feet_agriculture_gross = floor_area_ratio_agriculture * square_feet_per_acre -building_square_feet_armed_forces_gross Total square feet of building associated with armed forces uses, including common areas. This is derived from the armed forces Floor Area Ratio (FAR). building_square_feet_armed_forces_gross = floor_area_ratio_armed_forces * square_feet_per_acre -building_square_feet_retail_net The leasable retail portion of building; that is, the amount contained within retail stores, excluding indoor building common areas. building_square_feet_retail_net = building_square_feet_retail_gross * retail_percent_efficiency -building_square_feet_office_net The leasable office portion of building, excluding indoor building common areas. building_square_feet_office_net = building_square_feet_office_gross * office_percent_efficiency -building_square_feet_industrial_net The leasable industrial portion of building; excluding indoor building common areas. building_square_feet_industrial_net = building_square_feet_industrial_gross * industrial_percent_efficiency -building_square_feet_agriculture_net The leasable agricultural portion of building; excluding indoor building common areas. building_square_feet_agriculture_net = building_square_feet_agriculture_gross * agriculture_percent_efficiency -building_square_feet_armed_forces_net After excluding building common areas, the remainder of a building used for armed forces employees. building_square_feet_armed_forces_net = building_square_feet_agriculture_gross * agriculture_percent_efficiency -building_square_feet_detached_single_family The total square feet of single family detached homes is the sum of small lot and large lot single family detached home square footage. building_square_feet_detached_single_family = building_square_feet_detached_single_family_large_lot + building_square_feet_detached_single_family_small_lot -building_square_feet_detached_single_family_small_lot The total square feet of single family detached small lot homes is derived from the total number of single family detached small lot DU and the average square feet per SFSLDU. building_square_feet_detached_single_family_small_lot = detached_single_family_small_lot_density * square_feet_per_du_average -building_square_feet_detached_single_family_large_lot The total square feet of single family detached large lot homes is derived from the total number of single family detached large lot DU and the average square feet per SFLLDU. building_square_feet_detached_single_family_large_lot = detached_single_family_large_lot_density * square_feet_per_du_average -building_square_feet_attached_single_family The total square feet of single family attached homes is derived from the total number of single family attached DU and the average square feet per attached DU. building_square_feet_attached_single_family = attached_single_family_density * square_feet_per_du_average -building_square_feet_multifamily The total square feet of multifamily housing is derived from the total number of multifamily DU and the average square feet per multifamily DU. building_square_feet_multifamily = multifamily_density * square_feet_per_du_average -building_square_feet_residential_commons The non-leasable portion of a residential building; that is, the amount contained within indoor residential common areas, outside of private dwelling units. This is calculated based on total gross residential built up area and the residential percent efficiency. building_square_feet_residential_commons = residential_building_square_feet_gross * (1 - residential_percent_efficiency) -parcel_square_feet Parcel_square_feet is derived directly from parcel_acres by converting from acres to square feet. -parcel_acres Parcel_acres AKA developable land, can be “sensed” from GIS data, actually describing the amount of acreage contained within a parcel or parcels within a study area. It can be derived based on urban form characteristics, for instance by multiplying block length by block width. Or, it can be derived based on a net-to-gross ratio. -parcel_acres_residential The total acres of parcels containing only residential uses is calculated by summing the area of parcels containing buildings with only residential uses. parcel_acres_residential = total_land_acres * SUM(residential_parcel_percentage) -parcel_acres_residential_detached_single_family_small_lot The total acres of parcels containing only residential single family large lot development is calculated by summing the area of large-lot parcels containing single family buildings. parcel_acres_residential_detached_single_family_large_lot = total_land_acres * Sum(residential_detached_single_family_large_lot_parcel_percentage) -parcel_acres_residential_detached_single_family_large_lot The total acres of parcels containing only residential single family small lot development is calculated by summing the area of small-lot parcels containing single family buildings. parcel_acres_residential_detached_single_family_small_lot = total_land_acres * Sum(residential_detached_single_family_small_lot_parcel_percentage) -parcel_acres_residential_attached_single_family The total acres of parcels containing only attached residential single family development is calculated by summing the area of parcels containing single family attached buildings. parcel_acres_residential_attached_single_family = total_land_acres * Sum(residential_attached_single_family_parcel_percentage) -parcel_acres_residential_multifamily The total acres of parcels containing residential multifamily development is calculated by summing the area of parcels containing multifamily buildings. parcel_acres_residential_multifamily = total_land_acres Sum(residential_multifamily_parcel_percentage) -parcel_acres_employment The total acres of parcels containing only employment uses is calculated by summing the area of parcels containing buildings with only employment uses. parcel_acres_employment = total_land_acres * Sum(employment_parcel_percentage) -parcel_acres_employment_office The total acres of parcels containing office employment development is calculated by summing the area of parcels containing buildings with an office use. parcel_acres_employment_office = total_land_acres * Sum(employment_office_parcel_percentage) -parcel_acres_employment_retail The total acres of parcels containing retail employment development is calculated by summing the area of parcels containing buildings with a retail use. parcel_acres_employment_retail = total_land_acres * Sum(employment_retail_parcel_percentage) -parcel_acres_employment_industrial The total acres of parcels containing industrial employment development is calculated by summing the area of parcels containing buildings with an industrial use. parcel_acres_employment_industrial = total_land_acres * Sum(employment_industrial_parcel_percentage) -parcel_acres_employment_agricultural The total acres of parcels containing agricultural employment development is calculated by summing the area of parcels containing buildings with an agricultural use. parcel_acres_employment_agricultural = total_land_acres * Sum(employment_agricultural_parcel_percentage) -parcel_acres_employment_armed_forces The total acres of parcels containing armed forces employment development is calculated by summing the area of parcels containing buildings with an armed forces use. parcel_acres_employment_armed_forces = total_land_acres * Sum(employment_armed_forces_parcel_percentage) -parcel_acres_employment_mixed The total acres of parcels containing a mix of employment uses is calculated by summing the area of parcels containing buildings with a mix of employment uses. parcel_acres_employment_mixed = total_land_acres * Sum(employment_mixed_parcel_percentage) -parcel_acres_mixed_use The total acres of parcels containing a mix of residential and employment uses is calculated by summing the area of parcels containing buildings with both residential and employment uses. parcel_acres_mixed_use = total_land_acres * Sum(mixed_use_parcel_percentage) -parcel_acres_mixed_use_with_office The total acres of parcels containing a mix of residential and employment uses, including office employment, is calculated by summing the area of parcels containing buildings with a mix of residential and employment uses with more than zero office employees. parcel_acres_mixed_use_with_office = total_land_acres Sum(mixed_use_with_office_parcel_percentage) -parcel_acres_mixed_use_no_office The total acres of parcels containing a mix of residential and employment uses, excluding office employment, is calculated by summing the area of parcels containing buildings with a mix of residential and employment uses with no office employees. parcel_acres_mixed_use_no_office = total_land_acres * Sum(mixed_use_no_office_parcel_percentage) -residential_square_feet_irrigated The total square feet of irrigated yard on residential parcels; calculated by summing the average amount of irrigated yard for each participating residential building. residential_square_feet_irrigated = Sum(residential_parcel_irrigated_square_feet) -employment_square_feet_irrigated The total square feet of irrigated landscaping on employment parcels; calculated by summing the average amount of irrigated landscaping for each participating employment building. employment_square_feet_irrigated = Sum(employment_parcel_irrigated_square_feet) -land_percentage_mixed_use The percentage of total acres containing a mix of residential and employment parcels; this is calculated based on total area and the area of parcels containing buildings with both residential and employment uses. land_percentage_mixed_use = parcel_acres_mixed_use / total_land_acres -land_percentage_residential The percentage of total acres containing solely residential parcels; this is calculated based on total area and the area of parcels containing buildings with residential-only uses. land_percentage_residential = parcel_acres_residential / total_land_acres -land_percentage_employment The percentage of total acres containing solely employment parcels; this is calculated based on total area and the area of parcels containing buildings with employment-only uses. land_percentage_employment = parcel_acres_employment / total_land_acres -urban_land_development_category The urban Land Development Category (LDC) occurs within an urban design context including more than 150 walkable street intersections per square mile for a place type. It also features a high amount of residential and employment density. The LDC describes the most walkable, transit-oriented, low-Vehicle-Miles-Traveled (VMT) city environments. -compact_land_development_category The intent of the compact LDC is to describe walkable, somewhat-transit-oriented, low-to-medium-VMT neighborhood environments. -standard_land_development_category The intent of the standard LDC is to describe less-walkable, automobile-oriented, medium-to-high-VMT suburban environments. -colorhex -=========================================================== ============================================================================================================================================================================================================================================================================================================================================================= ================================================================================================================================================================================== - diff --git a/Urban_Footprint_Sphinx_Docs/code.rst b/Urban_Footprint_Sphinx_Docs/code.rst deleted file mode 100644 index 8d352b225..000000000 --- a/Urban_Footprint_Sphinx_Docs/code.rst +++ /dev/null @@ -1,8 +0,0 @@ -AutoGenerated Code -================== - -.. automodule:: keys - :members: - -.. automodule:: constants - :members: diff --git a/Urban_Footprint_Sphinx_Docs/conf.py b/Urban_Footprint_Sphinx_Docs/conf.py deleted file mode 100644 index 0ffd1ae21..000000000 --- a/Urban_Footprint_Sphinx_Docs/conf.py +++ /dev/null @@ -1,290 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Urban Footprint documentation build configuration file, created by -# sphinx-quickstart on Tue Nov 27 15:34:30 2012. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('.')) -sys.path.append('../calthorpe/server/calthorpe/footprint/models') - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.graphviz', - 'inheritance_diagram'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Urban Footprint' -copyright = u'2012, Calthorpe' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '1.0' -# The full version, including alpha/beta/rc tags. -release = '1.0.0' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'UrbanFootprintdoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'UrbanFootprint.tex', u'Urban Footprint Documentation', - u'Calthorpe', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'urbanfootprint', u'Urban Footprint Documentation', - [u'Calthorpe'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------------ - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'UrbanFootprint', u'Urban Footprint Documentation', - u'Calthorpe', 'UrbanFootprint', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - - -# -- Options for Epub output --------------------------------------------------- - -# Bibliographic Dublin Core info. -epub_title = u'Urban Footprint' -epub_author = u'Calthorpe' -epub_publisher = u'Calthorpe' -epub_copyright = u'2012, Calthorpe' - -# The language of the text. It defaults to the language option -# or en if the language is not set. -#epub_language = '' - -# The scheme of the identifier. Typical schemes are ISBN or URL. -#epub_scheme = '' - -# The unique identifier of the text. This can be a ISBN number -# or the project homepage. -#epub_identifier = '' - -# A unique identification for the text. -#epub_uid = '' - -# A tuple containing the cover image and cover page html template filenames. -#epub_cover = () - -# HTML files that should be inserted before the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_pre_files = [] - -# HTML files shat should be inserted after the pages created by sphinx. -# The format is a list of tuples containing the path and title. -#epub_post_files = [] - -# A list of files that should not be packed into the epub file. -#epub_exclude_files = [] - -# The depth of the table of contents in toc.ncx. -#epub_tocdepth = 3 - -# Allow duplicate toc entries. -#epub_tocdup = True diff --git a/Urban_Footprint_Sphinx_Docs/index.rst b/Urban_Footprint_Sphinx_Docs/index.rst deleted file mode 100644 index 1e9ff949f..000000000 --- a/Urban_Footprint_Sphinx_Docs/index.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. Urban Footprint documentation master file, created by - sphinx-quickstart on Tue Nov 27 15:34:30 2012. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Urban Footprint's Documentation -====================================================================================== - -General Documentation: - -- Urban Footprint Technical Summary (download PDF_) -- Tutorials - -.. _PDF: http://www.calthorpe.com/files/Rapid%20Fire%20V%202.0%20Tech%20Summary_0.pdf - -Development Documentation: - -.. toctree:: - :maxdepth: 100 - - code - building_definition - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/Urban_Footprint_Sphinx_Docs/inheritance_diagram.py b/Urban_Footprint_Sphinx_Docs/inheritance_diagram.py deleted file mode 100644 index 407fc13ff..000000000 --- a/Urban_Footprint_Sphinx_Docs/inheritance_diagram.py +++ /dev/null @@ -1,407 +0,0 @@ -""" -Defines a docutils directive for inserting inheritance diagrams. - -Provide the directive with one or more classes or modules (separated -by whitespace). For modules, all of the classes in that module will -be used. - -Example:: - - Given the following classes: - - class A: pass - class B(A): pass - class C(A): pass - class D(B, C): pass - class E(B): pass - - .. inheritance-diagram: D E - - Produces a graph like the following: - - A - / \ - B C - / \ / - E D - -The graph is inserted as a PNG+image map into HTML and a PDF in -LaTeX. -""" - -import inspect -import os -import re -import subprocess -try: - from hashlib import md5 -except ImportError: - from md5 import md5 - -from docutils.nodes import Body, Element -from docutils.parsers.rst import directives -from sphinx.roles import xfileref_role - -def my_import(name): - """Module importer - taken from the python documentation. - - This function allows importing names with dots in them.""" - - mod = __import__(name) - components = name.split('.') - for comp in components[1:]: - mod = getattr(mod, comp) - return mod - -class DotException(Exception): - pass - -class InheritanceGraph(object): - """ - Given a list of classes, determines the set of classes that - they inherit from all the way to the root "object", and then - is able to generate a graphviz dot graph from them. - """ - def __init__(self, class_names, show_builtins=False): - """ - *class_names* is a list of child classes to show bases from. - - If *show_builtins* is True, then Python builtins will be shown - in the graph. - """ - self.class_names = class_names - self.classes = self._import_classes(class_names) - self.all_classes = self._all_classes(self.classes) - if len(self.all_classes) == 0: - raise ValueError("No classes found for inheritance diagram") - self.show_builtins = show_builtins - - py_sig_re = re.compile(r'''^([\w.]*\.)? # class names - (\w+) \s* $ # optionally arguments - ''', re.VERBOSE) - - def _import_class_or_module(self, name): - """ - Import a class using its fully-qualified *name*. - """ - try: - path, base = self.py_sig_re.match(name).groups() - except: - raise ValueError( - "Invalid class or module '%s' specified for inheritance diagram" % name) - fullname = (path or '') + base - path = (path and path.rstrip('.')) - if not path: - path = base - try: - module = __import__(path, None, None, []) - # We must do an import of the fully qualified name. Otherwise if a - # subpackage 'a.b' is requested where 'import a' does NOT provide - # 'a.b' automatically, then 'a.b' will not be found below. This - # second call will force the equivalent of 'import a.b' to happen - # after the top-level import above. - my_import(fullname) - - except ImportError: - raise ValueError( - "Could not import class or module '%s' specified for inheritance diagram" % name) - - try: - todoc = module - for comp in fullname.split('.')[1:]: - todoc = getattr(todoc, comp) - except AttributeError: - raise ValueError( - "Could not find class or module '%s' specified for inheritance diagram" % name) - - # If a class, just return it - if inspect.isclass(todoc): - return [todoc] - elif inspect.ismodule(todoc): - classes = [] - for cls in todoc.__dict__.values(): - if inspect.isclass(cls) and cls.__module__ == todoc.__name__: - classes.append(cls) - return classes - raise ValueError( - "'%s' does not resolve to a class or module" % name) - - def _import_classes(self, class_names): - """ - Import a list of classes. - """ - classes = [] - for name in class_names: - classes.extend(self._import_class_or_module(name)) - return classes - - def _all_classes(self, classes): - """ - Return a list of all classes that are ancestors of *classes*. - """ - all_classes = {} - - def recurse(cls): - all_classes[cls] = None - for c in cls.__bases__: - if c not in all_classes: - recurse(c) - - for cls in classes: - recurse(cls) - - return all_classes.keys() - - def class_name(self, cls, parts=0): - """ - Given a class object, return a fully-qualified name. This - works for things I've tested in matplotlib so far, but may not - be completely general. - """ - module = cls.__module__ - if module == '__builtin__': - fullname = cls.__name__ - else: - fullname = "%s.%s" % (module, cls.__name__) - if parts == 0: - return fullname - name_parts = fullname.split('.') - return '.'.join(name_parts[-parts:]) - - def get_all_class_names(self): - """ - Get all of the class names involved in the graph. - """ - return [self.class_name(x) for x in self.all_classes] - - # These are the default options for graphviz - default_graph_options = { - "rankdir": "LR", - "size": '"8.0, 12.0"' - } - default_node_options = { - "shape": "box", - "fontsize": 10, - "height": 0.25, - "fontname": "Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans", - "style": '"setlinewidth(0.5)"' - } - default_edge_options = { - "arrowsize": 0.5, - "style": '"setlinewidth(0.5)"' - } - - def _format_node_options(self, options): - return ','.join(["%s=%s" % x for x in options.items()]) - def _format_graph_options(self, options): - return ''.join(["%s=%s;\n" % x for x in options.items()]) - - def generate_dot(self, fd, name, parts=0, urls={}, - graph_options={}, node_options={}, - edge_options={}): - """ - Generate a graphviz dot graph from the classes that - were passed in to __init__. - - *fd* is a Python file-like object to write to. - - *name* is the name of the graph - - *urls* is a dictionary mapping class names to http urls - - *graph_options*, *node_options*, *edge_options* are - dictionaries containing key/value pairs to pass on as graphviz - properties. - """ - g_options = self.default_graph_options.copy() - g_options.update(graph_options) - n_options = self.default_node_options.copy() - n_options.update(node_options) - e_options = self.default_edge_options.copy() - e_options.update(edge_options) - - fd.write('digraph %s {\n' % name) - fd.write(self._format_graph_options(g_options)) - - for cls in self.all_classes: - if not self.show_builtins and cls in __builtins__.values(): - continue - - name = self.class_name(cls, parts) - - # Write the node - this_node_options = n_options.copy() - url = urls.get(self.class_name(cls)) - if url is not None: - this_node_options['URL'] = '"%s"' % url - fd.write(' "%s" [%s];\n' % - (name, self._format_node_options(this_node_options))) - - # Write the edges - for base in cls.__bases__: - if not self.show_builtins and base in __builtins__.values(): - continue - - base_name = self.class_name(base, parts) - fd.write(' "%s" -> "%s" [%s];\n' % - (base_name, name, - self._format_node_options(e_options))) - fd.write('}\n') - - def run_dot(self, args, name, parts=0, urls={}, - graph_options={}, node_options={}, edge_options={}): - """ - Run graphviz 'dot' over this graph, returning whatever 'dot' - writes to stdout. - - *args* will be passed along as commandline arguments. - - *name* is the name of the graph - - *urls* is a dictionary mapping class names to http urls - - Raises DotException for any of the many os and - installation-related errors that may occur. - """ - try: - dot = subprocess.Popen(['dot'] + list(args), - stdin=subprocess.PIPE, stdout=subprocess.PIPE, - close_fds=True) - except OSError: - raise DotException("Could not execute 'dot'. Are you sure you have 'graphviz' installed?") - except ValueError: - raise DotException("'dot' called with invalid arguments") - except: - raise DotException("Unexpected error calling 'dot'") - - self.generate_dot(dot.stdin, name, parts, urls, graph_options, - node_options, edge_options) - dot.stdin.close() - result = dot.stdout.read() - returncode = dot.wait() - if returncode != 0: - raise DotException("'dot' returned the errorcode %d" % returncode) - return result - -class inheritance_diagram(Body, Element): - """ - A docutils node to use as a placeholder for the inheritance - diagram. - """ - pass - -def inheritance_diagram_directive(name, arguments, options, content, lineno, - content_offset, block_text, state, - state_machine): - """ - Run when the inheritance_diagram directive is first encountered. - """ - node = inheritance_diagram() - - class_names = arguments - - # Create a graph starting with the list of classes - graph = InheritanceGraph(class_names) - - # Create xref nodes for each target of the graph's image map and - # add them to the doc tree so that Sphinx can resolve the - # references to real URLs later. These nodes will eventually be - # removed from the doctree after we're done with them. - for name in graph.get_all_class_names(): - refnodes, x = xfileref_role( - 'class', ':class:`%s`' % name, name, 0, state) - node.extend(refnodes) - # Store the graph object so we can use it to generate the - # dot file later - node['graph'] = graph - # Store the original content for use as a hash - node['parts'] = options.get('parts', 0) - node['content'] = " ".join(class_names) - return [node] - -def get_graph_hash(node): - return md5(node['content'] + str(node['parts'])).hexdigest()[-10:] - -def html_output_graph(self, node): - """ - Output the graph for HTML. This will insert a PNG with clickable - image map. - """ - graph = node['graph'] - parts = node['parts'] - - graph_hash = get_graph_hash(node) - name = "inheritance%s" % graph_hash - path = '_images' - dest_path = os.path.join(setup.app.builder.outdir, path) - if not os.path.exists(dest_path): - os.makedirs(dest_path) - png_path = os.path.join(dest_path, name + ".png") - path = setup.app.builder.imgpath - - # Create a mapping from fully-qualified class names to URLs. - urls = {} - for child in node: - if child.get('refuri') is not None: - urls[child['reftitle']] = child.get('refuri') - elif child.get('refid') is not None: - urls[child['reftitle']] = '#' + child.get('refid') - - # These arguments to dot will save a PNG file to disk and write - # an HTML image map to stdout. - image_map = graph.run_dot(['-Tpng', '-o%s' % png_path, '-Tcmapx'], - name, parts, urls) - return ('%s' % - (path, name, name, image_map)) - -def latex_output_graph(self, node): - """ - Output the graph for LaTeX. This will insert a PDF. - """ - graph = node['graph'] - parts = node['parts'] - - graph_hash = get_graph_hash(node) - name = "inheritance%s" % graph_hash - dest_path = os.path.abspath(os.path.join(setup.app.builder.outdir, '_images')) - if not os.path.exists(dest_path): - os.makedirs(dest_path) - pdf_path = os.path.abspath(os.path.join(dest_path, name + ".pdf")) - - graph.run_dot(['-Tpdf', '-o%s' % pdf_path], - name, parts, graph_options={'size': '"6.0,6.0"'}) - return '\n\\includegraphics{%s}\n\n' % pdf_path - -def visit_inheritance_diagram(inner_func): - """ - This is just a wrapper around html/latex_output_graph to make it - easier to handle errors and insert warnings. - """ - def visitor(self, node): - try: - content = inner_func(self, node) - except DotException, e: - # Insert the exception as a warning in the document - warning = self.document.reporter.warning(str(e), line=node.line) - warning.parent = node - node.children = [warning] - else: - source = self.document.attributes['source'] - self.body.append(content) - node.children = [] - return visitor - -def do_nothing(self, node): - pass - -def setup(app): - setup.app = app - setup.confdir = app.confdir - - app.add_node( - inheritance_diagram, - latex=(visit_inheritance_diagram(latex_output_graph), do_nothing), - html=(visit_inheritance_diagram(html_output_graph), do_nothing)) - app.add_directive( - 'inheritance-diagram', inheritance_diagram_directive, - False, (1, 100, 0), parts = directives.nonnegative_int) diff --git a/conf/celery/etc/default/celeryd b/conf/celery/etc/default/celeryd new file mode 100644 index 000000000..f90a2158d --- /dev/null +++ b/conf/celery/etc/default/celeryd @@ -0,0 +1,32 @@ +#number of nodes to start, here we have a single node +CELERYD_NODES="w1" +# or we could have three nodes: +#CELERYD_NODES="w1 w2 w3" +# Where to chdir at start. +CELERYD_CHDIR="/srv/calthorpe/urbanfootprint" + +# Python interpreter from environment. +ENV_PYTHON="/srv/calthorpe_env/bin/python" + +# How to call "manage.py celeryd_multi" +CELERYD_MULTI="$ENV_PYTHON $CELERYD_CHDIR/manage.py celeryd_multi" + +# How to call "manage.py celeryctl" +CELERYCTL="$ENV_PYTHON $CELERYD_CHDIR/manage.py celeryctl" + +# Extra arguments to celeryd 604800 => 7 days limit per process +CELERYD_OPTS="--time-limit=604800 --concurrency=8 -E -B" + +# Name of the celery config module. +CELERY_CONFIG_MODULE="celeryconfig" + +# %n will be replaced with the nodename. +CELERYD_LOG_FILE="/var/log/celery/%n.log" +CELERYD_PID_FILE="/var/run/celery/%n.pid" + +# Workers should run as an unprivileged user. +CELERYD_USER="calthorpe" +CELERYD_GROUP="www-data" + +# Name of the projects settings module. +export DJANGO_SETTINGS_MODULE="footprint.settings" diff --git a/conf/celery/etc/default/celeryev b/conf/celery/etc/default/celeryev new file mode 100644 index 000000000..908aef62e --- /dev/null +++ b/conf/celery/etc/default/celeryev @@ -0,0 +1,29 @@ +# Name of the projects settings module. +export DJANGO_SETTINGS_MODULE="footprint.settings" + +CELERY_BIN="/srv/calthorpe_env/bin/celery" +CELERY_APP=" + +# Where the Django project is. +CELERYD_CHDIR="/srv/calthorpe/urbanfootprint" + +CELERYEV_LOG_DIR="/var/log/celery" + + + +ENV_PYTHON="/srv/calthorpe_env/bin/python" + +VIRTUAL_ENV="/srv/calthorpe_env" + +# Path to celeryd +CELERYEV="$ENV_PYTHON /srv/calthorpe/urbanfootprint/manage.py" + +# Extra arguments to manage.py +CELERYEV_OPTS="celeryev" + +# Camera class to use (required) +CELERYEV_CAM="djcelery.snapshot.Camera" + +CELERYEV_USER="calthorpe" + +CELERYEV_GROUP="www-data" diff --git a/conf/etc/nginx/sites-available/calthorpe.nginx.dev b/conf/etc/nginx/sites-available/calthorpe.nginx.dev new file mode 100644 index 000000000..67e1eb332 --- /dev/null +++ b/conf/etc/nginx/sites-available/calthorpe.nginx.dev @@ -0,0 +1,90 @@ +server { + listen 80; + server_name localhost; + + # Max upload size + client_max_body_size 2G; + + #root /srv/www_amigoserver; + + + access_log /var/log/nginx/uf_dev.log; + + error_log /var/log/nginx/uf_dev.log; + + location /websockets { + proxy_pass http://127.0.0.1:8081; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location /socket.io { + proxy_pass http://127.0.0.1:8081; + proxy_set_header Host $host; + + proxy_set_header X-Real-IP $remote_addr; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 180s; + + proxy_read_timeout 180s; + } + + + location / { + + proxy_pass http://127.0.0.1:4020; + proxy_set_header Host $host; + + proxy_set_header X-Real-IP $remote_addr; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 180s; + + proxy_read_timeout 180s; + } + + location /downloads { + internal; + alias /srv/calthorpe_media/downloadable/; + } + + location /footprint { + + proxy_pass http://127.0.0.1:8000; + proxy_set_header Host $host; + + proxy_set_header X-Real-IP $remote_addr; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 180s; + + proxy_read_timeout 180s; + } + + location /admin { + + proxy_pass http://127.0.0.1:8000; + proxy_set_header Host $host; + + proxy_set_header X-Real-IP $remote_addr; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 180s; + + proxy_read_timeout 180s; + } + + location ~ ^/static/(grappelli|admin).*$ { + proxy_pass http://127.0.0.1:8000; + proxy_set_header Host $host; + + proxy_set_header X-Real-IP $remote_addr; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 180s; + + proxy_read_timeout 180s; + } +} diff --git a/conf/etc/nginx/sites-available/calthorpe.nginx.dev.scbuild b/conf/etc/nginx/sites-available/calthorpe.nginx.dev.scbuild new file mode 100644 index 000000000..ed1c4ed44 --- /dev/null +++ b/conf/etc/nginx/sites-available/calthorpe.nginx.dev.scbuild @@ -0,0 +1,72 @@ +server { + listen 80; + server_name localhost; + + + #root /srv/www_amigoserver; + + # Max upload size + client_max_body_size 2G; + + access_log /var/log/nginx/www.urbanfootprint.com.access.log; + + error_log /var/log/nginx/www.urbanfootprint.com.error.log; + + location /websockets { + proxy_pass http://127.0.0.1:8081; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } + + location /socket.io { + proxy_pass http://127.0.0.1:8081; + proxy_set_header Host $host; + + proxy_set_header X-Real-IP $remote_addr; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 180s; + + proxy_read_timeout 180s; + } + + + location / { + + proxy_pass http://127.0.0.1:4020; + proxy_set_header Host $host; + + proxy_set_header X-Real-IP $remote_addr; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 180s; + + proxy_read_timeout 180s; + } + + location /footprint { + + proxy_pass http://127.0.0.1:8000; + proxy_set_header Host $host; + + proxy_set_header X-Real-IP $remote_addr; + + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 180s; + + proxy_read_timeout 180s; + } + + + location /static/ { + proxy_pass http://127.0.0.1:8000; + autoindex off; + alias /srv/calthorpe_static/; + expires 30d; + add_header Pragma public; + add_header Cache-Control "public"; + } + +} + diff --git a/conf/etc/nginx/sites-available/calthorpe.nginx.prod b/conf/etc/nginx/sites-available/calthorpe.nginx.prod new file mode 100644 index 000000000..81995fb67 --- /dev/null +++ b/conf/etc/nginx/sites-available/calthorpe.nginx.prod @@ -0,0 +1,47 @@ +server { + listen 80 default_server; + server_name _; + + # Max upload size + client_max_body_size 2G; + + root /usr/share/nginx/www/; + + access_log /var/log/nginx/uf.prod.access.log; + error_log /var/log/nginx/uf.prod.error.log; + + + location / { + proxy_pass http://127.0.0.1:9001; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_connect_timeout 180s; + proxy_read_timeout 180s; + } + + location /static { + autoindex off; + alias /srv/calthorpe_static/; + expires 30d; + # add_header Pragma public; + # add_header Cache-Control "public"; + } + + location /downloads { + internal; + alias /srv/calthorpe_media/downloadable/; + } + + location /socket.io { + proxy_pass http://127.0.0.1:8081; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + } + + #location /robots.txt { alias /srv/calthorpe_static/robots.txt; } + #location /humans.txt { alias /srv/calthorpe_static/humans.txt; } + +} diff --git a/conf/etc/supervisor/conf.d/calthorpe.supervisor.dev b/conf/etc/supervisor/conf.d/calthorpe.supervisor.dev new file mode 100644 index 000000000..e0f16a46f --- /dev/null +++ b/conf/etc/supervisor/conf.d/calthorpe.supervisor.dev @@ -0,0 +1,49 @@ +[program:celery_worker] +environment=PATH="/srv/calthorpe_env/bin:/usr/bin",IS_CELERY="True" +command=/srv/calthorpe_env/bin/celery -A footprint worker -l debug -n footprint +directory=/srv/calthorpe/urbanfootprint/ +user=www-data +stdout_logfile=/var/log/supervisor/celery.log +stderr_logfile=/var/log/supervisor/celery.log +redirect_stderr=true +autostart=true +autorestart=true +startsecs=10 +stopwaitsecs=600 +killasgroup=true +numprocs=1 + +[program:celerybeat] +environment=PATH="/srv/calthorpe_env/bin:/usr/bin",IS_CELERY=True +command=/srv/calthorpe_env/bin/celery beat -A footprint --loglevel=info +directory=/srv/calthorpe/urbanfootprint/ +user=www-data +numprocs=1 +stdout_logfile=/var/log/supervisor/celery.log +stderr_logfile=/var/log/supervisor/celery.log +autostart=true +autorestart=true +startsecs=10 + +[program:celery_flower] +environment=PATH="/srv/calthorpe_env/bin:/usr/bin",IS_CELERY=True +command=/srv/calthorpe_env/bin/celery flower --broker=redis://localhost:6379/0 +user=www-data +numprocs=1 +stdout_logfile=/var/log/supervisor/celery.log +stderr_logfile=/var/log/supervisor/celery.log +autostart=true +autorestart=true +startsecs=10 + +[program:node_socketio] +command=/usr/bin/nodejs /srv/calthorpe/urbanfootprint/websockets/app.js +directory=/srv/calthorpe/urbanfootprint/websockets +user=www-data +stdout_logfile=/var/log/supervisor/nodesocketio.log +stderr_logfile=/var/log/supervisor/nodesocketio.log +redirect_stderr=true +autostart=true +autorestart=true +startsecs=10 +stopwaitsecs=600 diff --git a/conf/etc/supervisor/conf.d/calthorpe.supervisor.prod b/conf/etc/supervisor/conf.d/calthorpe.supervisor.prod new file mode 100644 index 000000000..29901c819 --- /dev/null +++ b/conf/etc/supervisor/conf.d/calthorpe.supervisor.prod @@ -0,0 +1,59 @@ +[program:calthorpe_www] +command=/srv/calthorpe_env/bin/python /srv/calthorpe/urbanfootprint/manage.py run_gunicorn -w 13 -b 127.0.0.1:9001 -t 180 +directory=/srv/calthorpe/urbanfootprint +user=www-data +autostart=true +autorestart=true +stdout_logfile=/var/log/supervisor/gunicorn.log +stderr_logfile=/var/log/supervisor/gunicorn.log +redirect_stderr=true + +[program:celery_worker] +environment=PATH="/srv/calthorpe_env/bin:/usr/bin" +command=/srv/calthorpe_env/bin/celery -A footprint worker -l warn -n footprint1 +directory=/srv/calthorpe/urbanfootprint/ +user=www-data +stderr_logfile=/var/log/supervisor/celery.log +stdout_logfile=/var/log/supervisor/celery.log +redirect_stderr=true +autostart=true +autorestart=true +startsecs=10 +stopwaitsecs=600 +killasgroup=true +numprocs=1 + +[program:celerybeat] +environment=PATH="/srv/calthorpe_env/bin" +command=/srv/calthorpe_env/bin/celery beat -A footprint --loglevel=info +directory=/srv/calthorpe/urbanfootprint/ +user=calthorpe +numprocs=1 +stdout_logfile=/var/log/supervisor/celery.log +stderr_logfile=/var/log/supervisor/celery.log +autostart=true +autorestart=true +startsecs=10 + +[program:celery_flower] +environment=PATH="/srv/calthorpe_env/bin" +command=/srv/calthorpe_env/bin/celery flower --broker=redis://localhost:6379/0 +user=www-data +numprocs=1 +stdout_logfile=/var/log/supervisor/celery.log +stderr_logfile=/var/log/supervisor/celery.log +autostart=true +autorestart=true +startsecs=10 + +[program:node_socketio] +command=/usr/bin/nodejs /srv/calthorpe/urbanfootprint/websockets/app.js +directory=/srv/calthorpe/urbanfootprint/websockets +user=www-data +stderr_logfile=/var/log/supervisor/socketio.log +stdout_logfile=/var/log/supervisor/socketio.log +redirect_stderr=true +autostart=true +autorestart=true +startsecs=10 +stopwaitsecs=600 \ No newline at end of file diff --git a/conf/etc/supervisor/conf.d/calthorpe.supervisor.ragis b/conf/etc/supervisor/conf.d/calthorpe.supervisor.ragis new file mode 100644 index 000000000..0a161cedd --- /dev/null +++ b/conf/etc/supervisor/conf.d/calthorpe.supervisor.ragis @@ -0,0 +1,61 @@ + +[program:beta_celeryd] +command=r/erver_env/bin/python /srv/beta_amigoserver/amigoserver/django/amigoserver/manage.py celeryd --loglevel=INFO -c 1 -B -E +directory=/srv/calthorpe/urbanfootprint/ +user=calthorpe +group=www-data +stdout_logfile=/var/log/supervisor/amigoserver_beta_celeryd.log +redirect_stderr=true +autostart=true +autorestart=true +startsecs=10 +stopwaitsecs=600 + +[program:celery_worker] +environment=PATH="/srv/calthorpe_env/bin" +command=/srv/calthorpe_env/bin/celery -A footprint worker -l debug -n footprint +directory=/srv/calthorpe/urbanfootprint/ +user=calthorpe +group=www-data +stdout_logfile=/var/log/supervisor/celery.log +redirect_stderr=true +autostart=true +autorestart=true +startsecs=10 +stopwaitsecs=600 +killasgroup=true +numprocs=1 + +[program:celerybeat] +environment=PATH="/srv/calthorpe_env/bin" +command=/srv/calthorpe_env/bin/celery beat -A footprint --loglevel=info +directory=/srv/calthorpe/urbanfootprint/ +user=calthorpe +numprocs=1 +stdout_logfile=/var/log/supervisor/celery.log +stderr_logfile=/var/log/supervisor/celery.log +autostart=true +autorestart=true +startsecs=10 + +[program:celery_flower] +environment=PATH="/srv/calthorpe_env/bin" +command=/srv/calthorpe_env/bin/celery flower --broker=redis://localhost:6379/0 +user=www-data +numprocs=1 +stdout_logfile=/var/log/supervisor/celery.log +stderr_logfile=/var/log/supervisor/celery.log +autostart=true +autorestart=true +startsecs=10 + +[program:node_socketio] +command=/usr/bin/nodejs /srv/calthorpe/urbanfootprint/websockets/app.js +directory=/srv/calthorpe/urbanfootprint/websockets +user=www-data +stdout_logfile=/var/log/supervisor/calthorpe_prod_nodesocketio.log +redirect_stderr=true +autostart=true +autorestart=true +startsecs=10 +stopwaitsecs=600 diff --git a/conf/etc/varnish/default.vcl.prod b/conf/etc/varnish/default.vcl.prod new file mode 100644 index 000000000..9b9773c7c --- /dev/null +++ b/conf/etc/varnish/default.vcl.prod @@ -0,0 +1,124 @@ +#st of upstream proxies we trust to set X-Forwarded-For correctly. +acl upstream_proxy { + "127.0.0.1"; +} + +# Content Server +backend default { + .host = "127.0.0.1"; + .port = "8082"; +} + +sub vcl_recv { + + #strip out any cookies from anything requested from static folder so it becomes + #candidate for caching + + if (req.url ~ "/static/" ) { + unset req.http.cookie; + } + + # Set the X-Forwarded-For header so the backend can see the original + # IP address. If one is already set by an upstream proxy, we'll just re-use that. + if (client.ip ~ upstream_proxy && req.http.X-Forwarded-For) { + set req.http.X-Forwarded-For = req.http.X-Forwarded-For; + } else { + set req.http.X-Forwarded-For = regsub(client.ip, ":.*", ""); + } + + # Do not cache these paths. + #if (req.url ~ "^/status\.php$" || + # req.url ~ "^/update\.php" || + # req.url ~ "^/install\.php" || + # req.url ~ "^/piwik\.php" || + # req.url ~ "^/admin/build/features" ) { + # return (pass); + #} + + # Pipe these paths directly to Apache for streaming. + #if (req.url ~ "^/admin/content/backup_migrate/export") { + # return (pipe); + #} + + # Deal with GET and HEAD requests only, everything else gets through + if (req.request != "GET" && + req.request != "HEAD") { + return (pass); + } + + # Always cache the following file types for all users. + #if (req.url ~ "(?i)\.(png|gif|jpeg|jpg|ico|swf|css|js|html|htm)(\?[a-z0-9]+)?$") { + # unset req.http.Cookie; + #} + + # Handle compression correctly. Different browsers send different + # "Accept-Encoding" headers, even though they mostly all support the same + # compression mechanisms. By consolidating these compression headers into + # a consistent format, we can reduce the size of the cache and get more hits. + # @see: http://varnish.projects.linpro.no/wiki/FAQ/Compression + if (req.http.Accept-Encoding) { + if (req.http.Accept-Encoding ~ "gzip") { + # If the browser supports it, we'll use gzip. + set req.http.Accept-Encoding = "gzip"; + } + else if (req.http.Accept-Encoding ~ "deflate" && req.http.user-agent !~ "MSIE") { + # Next, try deflate if it is supported. + set req.http.Accept-Encoding = "deflate"; + } + else { + # Unknown algorithm. Remove it and send unencoded. + unset req.http.Accept-Encoding; + } + } +} + +# When cached data is delivered to the client, set a bit of cache info. +sub vcl_deliver { + # Add cache hit data + if (obj.hits > 0) { + # If hit add hit count + set resp.http.X-Cache = "HIT"; + set resp.http.X-Cache-Hits = obj.hits; + } else { + set resp.http.X-Cache = "MISS"; + } + return (deliver); +} + +sub vcl_fetch { + + if (req.url ~ "/static/" ) { + unset beresp.http.set-cookie; + } + + # Varnish determined the object was not cacheable + if (beresp.ttl <= 0s) { + set beresp.http.X-Cacheable = "NO:Not Cacheable"; + + # You don't wish to cache content for logged in users + } elsif (req.http.Cookie ~ "(UserID|_session)") { + set beresp.http.X-Cacheable = "NO:Got Session"; + return(hit_for_pass); + + # You are respecting the Cache-Control=private header from the backend + } elsif (beresp.http.Cache-Control ~ "private") { + set beresp.http.X-Cacheable = "NO:Cache-Control=private"; + return(hit_for_pass); + + # Varnish determined the object was cacheable + } else { + set beresp.http.X-Cacheable = "YES"; + } + + #default vcl_fetch logic + if (beresp.ttl <= 0s || + beresp.http.Set-Cookie || + beresp.http.Vary == "*") { + /* + * Mark as "Hit-For-Pass" for the next 2 minutes + */ + set beresp.ttl = 120 s; + return (hit_for_pass); + } + return (deliver); +} diff --git a/fabfile/__init__.py b/fabfile/__init__.py new file mode 100644 index 000000000..8816099eb --- /dev/null +++ b/fabfile/__init__.py @@ -0,0 +1,12 @@ +__author__ = 'calthorpe_associates' + +import sys +import os + +sys.path.append(os.path.dirname(__file__)) +sys.path.append(os.path.dirname(os.path.dirname(__file__))) +sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) + +from .hosts import * +from .installation import * +from .management import * \ No newline at end of file diff --git a/fabfile/hadoop.py b/fabfile/hadoop.py new file mode 100644 index 000000000..6593c59d6 --- /dev/null +++ b/fabfile/hadoop.py @@ -0,0 +1,129 @@ +from fabric.context_managers import cd +from fabric.contrib.files import append, sed +from fabric.operations import sudo +from fabric.state import env + +__author__ = 'calthorpe_associates' + +HADOOP_VERSION = '1.0.3' + +from installation import JAVA_RESPOSITORY, JAVA_VERSION + +def install_hadoop(): + sudo('addgroup hadoop') + sudo('adduser --ingroup hadoop hduser') + sudo('ssh-keygen -t rsa -P "" -f /home/hduser/.ssh/id_rsa', user='hduser') + sudo('cat /home/hduser/.ssh/id_rsa.pub >> /home/hduser/.ssh/authorized_keys', user='hduser') + sudo('ssh-keyscan -H {0} >> /home/hduser/.ssh/known_hosts'.format(env.host), user='hduser') + sudo('wget -O /usr/local/hadoop-{0}.tar.gz ' + 'http://mirrors.ibiblio.org/apache/hadoop/core/hadoop-{0}/hadoop-{0}.tar.gz'.format(HADOOP_VERSION)) + with cd('/usr/local'): + sudo('tar xzf hadoop-{0}.tar.gz'.format(HADOOP_VERSION)) + sudo('chown -R hduser:hadoop hadoop-{0}'.format(HADOOP_VERSION)) + sudo('ln -s hadoop-{0} hadoop'.format(HADOOP_VERSION)) + # Edit .bashrc + # APPEND JAVA_HOME if it doesn't exist yet + append('/home/hduser/.bashrc', 'export JAVA_HOME=/usr/lib/jvm/{0}'.format(JAVA_VERSION), use_sudo=True) + append('/home/hduser/.bashrc', '''# Set Hadoop-related environment variables +export HADOOP_HOME=/usr/local/hadoop + +# Some convenient aliases and functions for running Hadoop-related commands +unalias fs &> /dev/null +alias fs="hadoop fs" +unalias hls &> /dev/null +alias hls="fs -ls" + +# If you have LZO compression enabled in your Hadoop cluster and +# compress job outputs with LZOP (not covered in this tutorial): +# Conveniently inspect an LZOP compressed file from the command +# line; run via: +# +# $ lzohead /hdfs/path/to/lzop/compressed/file.lzo +# +# Requires installed lzop command. +# +lzohead () { + hadoop fs -cat $1 | lzop -dc | head -1000 | less +} + +# Add Hadoop bin/ directory to PATH +export PATH=$PATH:$HADOOP_HOME/bin''', use_sudo=True) + sed('/usr/local/hadoop/conf/hadoop-env.sh', r'^# export JAVA_HOME=.*', + '# export JAVA_HOME=/usr/lib/jvm/{0}'.format(JAVA_VERSION), backup='', use_sudo=True) + sudo('mkdir -p /app/hadoop/tmp') + sudo('sudo chown hduser:hadoop /app/hadoop/tmp') + sudo('/usr/local/hadoop/bin/hadoop namenode -format', user='hduser') + # Remove the and append a configuration block + sed('/usr/local/hadoop/conf/core-site.xml', r'^', '', backup='', use_sudo=True) + append('/usr/local/hadoop/conf/core-site.xml', ''' + + hadoop.tmp.dir + /app/hadoop/tmp + A base for other temporary directories. + + + + fs.default.name + hdfs://localhost:54310 + The name of the default file system. A URI whose + scheme and authority determine the FileSystem implementation. The uris scheme determines the config property \(fs.SCHEME.impl\) naming + the FileSystem implementation class. The uris authority is used to + determine the host, port, etc. for a filesystem. + +''', use_sudo=True, escape=True) + + sed('/usr/local/hadoop/conf/mapred-site.xml', r'^', '', backup='', use_sudo=True) + append('/usr/local/hadoop/conf/mapred-site.xml', ''' + + + mapred.job.tracker + localhost:54311 + The host and port that the MapReduce job tracker runs + at. If "local", then jobs are run in-process as a single map + and reduce task. + + +''', use_sudo=True) + + sed('/usr/local/hadoop/conf/hdfs-site.xml', r'^', '', backup='', use_sudo=True) + append('/usr/local/hadoop/conf/hdfs-site.xml', ''' + + + dfs.replication + 1 + Default block replication. + The actual number of replications can be specified when the file is created. + The default is used if replication is not specified in create time. + + +''', use_sudo=True) + start_hadoop() + + +def configure_hadoop_cluster(): + all_hosts = env.all_hosts + for slave in env.roledefs['slave']: + sudo('ssh-copy-id -i /home/hduser/.ssh/id_rsa.pub hduser@{0}'.format(slave), user='hduser') + if (env.host in env.roledefs['master']): + configure_as_hadoop_master() + else: + configure_as_hadoop_slave() + + +def configure_as_hadoop_master(): + pass + + +def configure_as_hadoop_slave(): + master = env.roledefs['master'][0] + # Run this so that the slave automatically accepts the master on the first ssh, to avoid the prompt + sudo('ssh-keyscan -H {0} >> /home/hduser/.ssh/known_hosts'.format(master), user='hduser') + + +def start_hadoop(): + sudo('/usr/local/hadoop/bin/start-all.sh') + + +def stop_hadoop(): + sudo('/usr/local/hadoop/bin/stop-all.sh', user='hduser') + diff --git a/fabfile/hosts/__init__.py b/fabfile/hosts/__init__.py new file mode 100644 index 000000000..ef6513dcc --- /dev/null +++ b/fabfile/hosts/__init__.py @@ -0,0 +1,20 @@ +from footprint.settings import CALTHORPE_DATA_DUMP_LOCATION + +__author__ = 'calthorpe_associates' + +from fabric.api import task, env + +@task +def localhost(skip_ssh=False): + """ + Sets up environment to pretend that localhost is a remote server + """ + if not skip_ssh: + env.hosts = ['127.0.0.1'] + + env.user = env.deploy_user = 'calthorpe' + env.deploy_user = 'calthorpe' + env.virtualenv_directory = '/srv/calthorpe_env' + env.password = '[PASSWORD]' + env.DATA_DUMP_PATH = CALTHORPE_DATA_DUMP_LOCATION + env.dev = True \ No newline at end of file diff --git a/fabfile/installation.py b/fabfile/installation.py new file mode 100644 index 000000000..711b79255 --- /dev/null +++ b/fabfile/installation.py @@ -0,0 +1,793 @@ +# UrbanFootprint-California, Scenario Planning Model +# +# Copyright (C) 2012-2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Calthorpe Associates (urbanfootprint@calthorpe.com) +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +# + +# +#import fabric +import os +import sys + +# Need to append the sys path to make the fabric django contrib module work. Sadly these things must +# be done in this order to have the django_settings object available +from fabric.context_managers import shell_env + +from fabric.contrib import django + +django.settings_module('footprint.settings') + +from django.conf import settings as django_settings +from django.core.management import call_command + +import cuisine +from cuisine_postgresql import (postgresql_role_ensure, postgresql_database_ensure, run_as_postgres ) +from fabric.api import (cd, run, env, settings, task) +from fabric.operations import sudo, local, prompt +from fabric.contrib import console +from fabric.contrib.files import append, exists, sed + +from footprint.common.utils.postgres_utils import build_postgres_conn_string, postgres_env_password_loaded + +PROJ_VER = '4.8.0' +GEOS_VER = '3.3.8' +GDAL_VER = '1.10.1' + +PROJ_PATH = '/usr/local/proj/' + PROJ_VER +GEOS_PATH = '/usr/local/geos/' + GEOS_VER +GDAL_PATH = '/usr/local/gdal/' + GDAL_VER + +FILEGDBAPI_FILE = 'FileGDB_API_1_3-64.tar.gz' +FILEGDBAPI_LOCATION = 'http://downloads2.esri.com/Software/%s' % (FILEGDBAPI_FILE,) + +JAVA_VERSION = 'java-7-oracle' +JAVA_RESPOSITORY = 'ppa:webupd8team/java' + +POSTGIS_PATH = '/usr/share/postgresql/9.1/contrib/postgis-1.5/' + + +def set_paths(): + global ROOT, GIT_ROOT, BASE_PATH, PROJ_ROOT, PYTHON_INTERPRETER, WEBSOCKETS_ROOT, SERVER_ROOT, TEMP_DIR + + if 'env' in globals(): + env = globals()['env'] + else: + env = 'default' + + configuration = getattr(env, 'client', 'default') + configurations = django_settings.PATH_CONFIGURATIONS + if configuration in configurations: + path_dict = configurations[configuration] + else: + path_dict = configurations['default'] + + ROOT = path_dict['ROOT'] + GIT_ROOT = path_dict['GIT_ROOT'] + BASE_PATH = path_dict['BASE_PATH'] + PROJ_ROOT = path_dict['PROJ_ROOT'] + PYTHON_INTERPRETER = path_dict['PYTHON_INTERPRETER'] + WEBSOCKETS_ROOT = path_dict['WEBSOCKETS_ROOT'] + SERVER_ROOT = path_dict['SERVER_ROOT'] + + TEMP_DIR = django_settings.TEMP_DIR + +set_paths() + +def virtualenv(command): + sudo('source ' + env.virtualenv_directory + '/bin/activate && ' + command, user=env.deploy_user) + + +def manage_py(command): + sudo(PYTHON_INTERPRETER + ' ' + SERVER_ROOT + '/manage.py ' + command, user=env.deploy_user) + +@task +def setup_databases(): + + # create production postgres database and test database + # needs to match local_settings.py deployment values + + postgresql_role_ensure(env.deploy_user, env.password, superuser=True, createdb=True, createrole=True, + inherit=True, login=True) + + postgresql_database_ensure('urbanfootprint', owner=env.deploy_user, template='template_postgis') + + # the following two lines below should be enabled for postgis 2.0 (also remove template_postgis from above lines) + #run_as_postgres('psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS POSTGIS" urbanfootprint') + + +@task +def setup_node_env(): + with cd(WEBSOCKETS_ROOT): + # removes this + try: + sudo('rm -r carto') + except: + pass + run('npm install .') + run('git clone git://github.com/mapbox/carto.git') + # run('git clone https://github.com/mapbox/node-sqlite3.git') + # rune('git clone https://github.com/mapbox/millstone.git') + # sudo('npm install -g ./node-sqlite3') + sudo('npm install -g ./carto') + # sudo('npm install -g ./millstone') + sudo('chown -R {0}.www-data node_modules/'.format(env.deploy_user)) + + +@task +def setup_urbanfootprint(upgrade_env=True, erase=False): + """ + Runs all the steps necessary to configure urbanfootprint + """ + if erase: + sudo('rm {git_root}* -rf'.format(git_root=GIT_ROOT)) + set_paths() + print "ROOT = {0}\n".format(ROOT), \ + "GIT_ROOT = {0}\n".format(GIT_ROOT), \ + "BASE_PATH = {0}\n".format(BASE_PATH), \ + "PROJECT_PATH: {0}\n".format(PROJ_ROOT), \ + "WEBSOCKETS_ROOT: {0}\n".format(WEBSOCKETS_ROOT) + + + from fabfile.management import deploy + # Make sure deployment user exists and that the key is setup correctly + cuisine.user_ensure(env.deploy_user) + if env.user != env.deploy_user: + sudo('chsh -s /bin/bash {0}'.format(env.deploy_user)) + sudo('mkdir -p ~{0}/.ssh/'.format(env.deploy_user), user=env.deploy_user) + sudo('cp ~/.ssh/id_rsa* ~{0}/.ssh/'.format(env.deploy_user)) + sudo('chown {0}.{0} ~{0}/.ssh/id_rsa*'.format(env.deploy_user)) + sudo('chmod 600 ~{0}/.ssh/id_rsa'.format(env.deploy_user), user=env.deploy_user) + + # add UbuntuGIS repo + sudo('add-apt-repository ppa:ubuntugis/ubuntugis-unstable -y') + sudo('add-apt-repository ppa:chris-lea/node.js -y') + sudo('add-apt-repository ppa:chris-lea/nginx-devel -y') + + cuisine.package_update() + cuisine.package_upgrade() + + # using oracle's jdk for good compatibility + # intentionally not install postgresql-9.1-postgis until we can upgrade to django 1.5.x and postgis 2.0 + cuisine.package_ensure( + 'build-essential openjdk-6-jre openjdk-6-jdk postgresql git python-software-properties proj libproj-dev ' + 'python-pip python-virtualenv python-dev virtualenvwrapper postgresql-server-dev-9.1 ' + 'gdal-bin libgdal1-dev nginx varnish supervisor redis-server curl python-gdal nodejs graphviz-dev graphviz' + # 'libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-python-dev libboost-regex-dev ' + # 'libboost-system-dev libboost-thread-dev + ) + + #install older postgis + create_template_postgis() + + cuisine.group_user_ensure("www-data", env.deploy_user) + cuisine.group_user_ensure("sudo", env.deploy_user) + + # setup deployment user git settings #TODO: make more sense of this... probably shouldn't be this for all setups + sudo('su {0} -c "git config --global user.email \"deploy@calthorpe.com\""'.format(env.deploy_user)) + sudo('su {0} -c "git config --global user.name \"Calthorpe Deployment\""'.format(env.deploy_user)) + + # these directories should be owned by calthorpe and the www-data user + dirs_to_create = [ + GIT_ROOT, + '/srv/calthorpe_static', + '/srv/calthorpe_media', + '/srv/calthorpe_media/cartocss', + '/srv/calthorpe_media/uploads', + '/tmp/stache/' + ] + + # create folders for calthorpe deployment + for dir in dirs_to_create: + sudo('mkdir -p {0}'.format(dir)) + sudo('chmod +t {0}'.format(dir)) + sudo('chown -R {user}.www-data {dir}'.format(dir=dir, user=env.deploy_user)) + + sudo('chmod g+w -R /srv/calthorpe_media') + + #create virtualenv + if not cuisine.dir_exists(env.virtualenv_directory): + sudo("virtualenv {env}".format(env=env.virtualenv_directory)) + sudo('chown -R {user}.www-data {env}'.format(user=env.deploy_user, env=env.virtualenv_directory)) + + install_mapnik() + install_osgeo() + # clone repo if needed + if not cuisine.dir_exists(BASE_PATH): + with cd(GIT_ROOT): + sudo('su {0} -c "git clone git@bitbucket.org:calthorpe/urbanfootprint.git"'.format(env.deploy_user)) + sudo('chown -R {user}.www-data {BASE_PATH}/..'.format(user=env.deploy_user, BASE_PATH=BASE_PATH)) + with cd('urbanfootprint'): + sudo('git submodule init') + sudo('git submodule update') + + setup_databases() + + with cd(PROJ_ROOT): + if not exists('local_settings.py'): + sudo('ln -s local_settings.py.{CLIENT} local_settings.py'.format(CLIENT=env.client)) + + setup_node_env() + # update varnish default port + #sed('/etc/default/varnish', r'^DAEMON_OPTS="-a :6081', 'DAEMON_OPTS="-a :80', use_sudo=True) + + # soft link all configuration files + with cd('/etc/varnish'): + sudo('rm -f default.vcl') + #sudo('ln -s /srv/calthorpe/urbanfootprint/calthorpe/server/conf/etc/varnish/default.vcl.prod default.vcl') + + nginx_configure() + + with cd('/etc/supervisor/conf.d'): + sudo('rm -f calthorpe.conf') + # Link the appropriate supervisor config file. dev omits a web server, and the log files are different + supervisor_conf_ext = 'dev' if env.dev else 'prod' + + link_supervisor_config_path = "ln -s {BASE_PATH}/conf/etc/supervisor/conf.d/calthorpe.supervisor.{supervisor_extension} calthorpe.conf" + sudo(link_supervisor_config_path.format(BASE_PATH=BASE_PATH, supervisor_extension=supervisor_conf_ext)) + + install_sproutcore() + + # trigger deploy to update virtualenv and restart services + deploy(upgrade_env=upgrade_env) + patch_django_layermapping() + + +@task +def setup_jenkins(): + """ + Runs all the steps necessary to configure Jenkins CI server + """ + # removes previous versions of jenkins to prevent conflict + sudo("apt-get remove --purge jenkins*") + + # add keys and latest/greatest debian repo from jenkins site + # sudo('wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -') + # sudo('echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list') + run('wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -') + sudo("echo deb http://pkg.jenkins-ci.org/debian binary/ > /etc/apt/sources.list.d/jenkins.list'") + + sudo('apt-get update') + sudo('apt-get install jenkins --upgrade') + + # add jenkins user to the shadow group + sudo('usermod -a -G shadow jenkins') + + # change jenkins default port to 9000apt- + sed('/etc/default/jenkins', before='^HTTP_PORT=.*', after='HTTP_PORT=9000', + backup='', use_sudo=True) + + sudo('service jenkins restart') + + #setup_urbanfootprint() + # setup jenkins user git settingsls + + #sudo('su jenkins -c "git config --global user.email \"build@calthorpe.com\""') + #sudo('su jenkins -c "git config --global user.name \"Calthorpe Build\""') + + # create folders for jenkin's Calthorpe-build job + #sudo('mkdir -p /srv/jenkins') + #sudo('chmod +t /srv/jenkins')alter + #sudo('mkdir -p /srv/jenkins/static') # needs to match local_settings.py.jenkins value + #sudo('mkdir -p /srv/jenkins/media') # needs to match local_settings.py.jenkins value + #sudo('chown -R jenkins.www-data /srv/jenkins') + + # create jenkins' postgres database and test database + postgresql_role_ensure('jenkins', 'JNKNZ', superuser=True, createdb=True, createrole=True, + inherit=True, login=True) + + #postgresql_database_ensure('jenkins', owner='jenkins') + #run_as_postgres('psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS POSTGIS" jenkins') + + #postgresql_database_ensure('test_jenkins', owner='jenkins') + #run_as_postgres('psql -U postgres -c "CREATE EXTENSION IF NOT EXISTS POSTGIS" test_jenkins') + + +@task +def publish_datadump(): + """ + Generates a local datadump and publishes it to the requested servers + """ + + call_command('create_datadump') + + # Make sure remote folders exist + sudo('mkdir -p {0}'.format(env.DATA_DUMP_PATH)) + sudo('chown -R {0}.www-data {1}'.format(env.user, env.DATA_DUMP_PATH)) + + # rsync media files to remote server + rsync_cmd = 'rsync --delete -rapthzv -e ssh' + + remote_server_conn_string = '{user}@{host}'.format( + user=env.user, + host=env.host_string) + + absolute_remote_data_dump_folder = '{remote_server_conn_string}:{data_dump_folder}'.format( + remote_server_conn_string=remote_server_conn_string, + data_dump_folder=env.DATA_DUMP_PATH) + + + # rsync local media folder into remote data dump folder + + local('{rsync} {local_media_folder} {absolute_remote_data_dump_folder}/media'.format( + rsync=rsync_cmd, + absolute_remote_data_dump_folder=absolute_remote_data_dump_folder, + local_media_folder=django_settings.MEDIA_ROOT)) + + # rsync local postgres datadump file into remote data dump location + + local_dump_file = os.path.join(django_settings.CALTHORPE_DATA_DUMP_LOCATION, 'pg_dump.dmp') + + local('{rsync} {local_dump_file} {absolute_remote_data_dump_folder}/pg_dump.dmp '.format( + rsync=rsync_cmd, + absolute_remote_data_dump_folder=absolute_remote_data_dump_folder, + local_dump_file=local_dump_file)) + + +@task +def fetch_datadump(force_local_db_destroy=False, use_local=False): + """ + Sync local database and media folder with official data_dump + 'fetch_datadump:force_local_db_destroy:True' to not prompt for db destruction confirmation + 'fetch_datadump:use_local:True avoid going through the ssh wire + """ + + db = django_settings.DATABASES['default'] + + if not force_local_db_destroy: + msg = 'You are DESTROYING the local "{dbname}" database! Continue?'.format( + dbname=db['NAME']) + + accepted_database_destroy = console.confirm(msg, default=False) + + if not accepted_database_destroy: + print 'Aborting fetch_datadump()' + return + + rsync_cmd = 'rsync --delete -rapthzv -e ssh' + + if use_local: + # it is a local grab, skip the fetching of resources across the wire + + absolute_remote_data_dump_folder = '{data_dump_folder}'.format( + data_dump_folder=env.DATA_DUMP_PATH) + + else: + # do remote grab + remote_server_conn_string = '{user}@{host}'.format( + user=env.user, + host=env.host_string) + + absolute_remote_data_dump_folder = '{remote_server_conn_string}:{data_dump_folder}'.format( + remote_server_conn_string=remote_server_conn_string, + data_dump_folder=env.DATA_DUMP_PATH) + + # rsync remote media folder (or local dump folder) into local + + local('{rsync} {absolute_remote_data_dump_folder}/media {local_media_folder}'.format( + rsync=rsync_cmd, + absolute_remote_data_dump_folder=absolute_remote_data_dump_folder, + local_media_folder=django_settings.MEDIA_ROOT)) + + # rsync postgres datadump file into local folder + # The reason we don't use tempfile.gettempdir() is that we always want the file to exist + # in the same place so we can take advantage of rsync's delta file-chunk speedup. In OSX, + # after every reboot, gettempdir returns a different directory defeating the point of using + # rsync. We use the '/tmp/' folder instead + + local_dump_file = os.path.join(TEMP_DIR, 'pg_dump.dmp') + + local('{rsync} {absolute_remote_data_dump_folder}/pg_dump.dmp {local_dump_file}'.format( + rsync=rsync_cmd, + absolute_remote_data_dump_folder=absolute_remote_data_dump_folder, + local_dump_file=local_dump_file)) + + with postgres_env_password_loaded(db): + db_conn_string = build_postgres_conn_string(db) + + # Some versions of postgres do not have --if-exists, so just ignore the error if it doesn't exist + with settings(warn_only=True): + local('dropdb {db_conn_string}'.format(db_conn_string=db_conn_string)) + + local('createdb -O {db_user} {db_conn_string}'.format(db_user=db['USER'], db_conn_string=db_conn_string)) + + # try to catch the typical error of not having the amigocloud role defined + with settings(warn_only=True): + result = local('pg_restore {db_conn_string} -d {dbname} {local_dump_file}'.format( + db_conn_string=build_postgres_conn_string(db, omit_db=True), + dbname=db['NAME'], + local_dump_file=local_dump_file)) + + if result.failed: + print "ERROR: You probably don't have 'calthorpe' ROLE defined. Fix by executing:" + print "CREATE ROLE calthorpe; GRANT calthorpe to {user};".format(user=db['USER']) + + raise SystemExit() + + +@task +def deploy_data(): + """ + Logs into remote machine and loads the data_load that has been published to the same machine + """ + + # stop any connections to the db + with settings(warn_only=True): + sudo('supervisorctl stop all') + sudo('/etc/init.d/supervisor stop') + + with cd(PROJ_ROOT): + fab_cmd = env.virtualenv_directory + '/bin/fab' + sudo('{fab_cmd} localhost:skip_ssh=True fetch_datadump:use_local=True,force_local_db_destroy=True'.format( + fab_cmd=fab_cmd), user=env.deploy_user) + + if os.path.exists('/srv/calthorpe_media'): + sudo('rm -r /srv/calthorpe_media') + sudo('cp -R /srv/datadump/media/calthorpe_media /srv/') + directory_permissions() + + # start connections to the db + sudo('/etc/init.d/supervisor start') + sudo('supervisorctl start all') + +@task +def directory_permissions(): + sudo('chown calthorpe:www-data /srv/calthorpe_media -R') + sudo('chmod 777 /srv/calthorpe_media -R') + + +@task +def public_key(): + """ + prints out public ssh key to stdout + """ + + if not exists('~/.ssh/id_rsa.pub'): + print 'Key does not exist. Generating new one...' + run('ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa') + + print "\n\n Please make sure the following key is listed as a deployment key\n" \ + " in the repo if you want to be able to do setup_urbanfootprint\n\n" + run('cat ~/.ssh/id_rsa.pub') + + +@task +def install_sproutcore(): + """ + get all Ruby rvm dependencies working alongside sproutcore + we want to do a multiuser install of rvm (hence why we pipe to `sudo bash` instead of just `bash`) + Also, we intentionally use the env.user (because he can sudo) and not env.deploy_user (that cannot sudo). + :return: + """ + # try: + # run('sproutcore') + # except: + try: + sudo('apt-get remove --purge ruby-rvm ruby --yes') + sudo('rm -rf /usr/share/ruby-rvm /etc/rmvrc /etc/profile.d/rvm.sh') + sudo('apt-get install build-essential --yes') + sudo('\curl -L https://get.rvm.io | bash -s stable --ruby --autolibs=3 --ruby=1.9.3') + sudo('apt-get install -y build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g ' + 'zlib1g-dev libssl-dev libyaml-dev libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool ' + 'bison subversion pkg-config sqlite3 libsqlite3-dev') + sudo('curl -L https://get.rvm.io | bash -s stable --ruby') # modified to install just to the calthorpe user + + append('.bashrc', ''' + [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm" # This loads RVM into a shell session''', + use_sudo=True) + + #for local setup: [[ -s "/usr/local/rvm/scripts/rvm" ]] && source "/usr/local/rvm/scripts/rvm" # This loads RVM into a shell session + + run('source /usr/local/rvm/scripts/rvm') + + sudo("rvm install ruby-1.9.3") + sudo("rvm use 1.9.3") + sudo("rvm --default use 1.9.3") + sudo("gem install sproutcore") + + cuisine.group_user_ensure('rvm', env.user) + cuisine.group_user_ensure('rvm', env.deploy_user) + except Exception, E: + print E + + +@task +def install_mapnik(): + """ + because mapnik requires some special steps to get set up, we do them before the rest of the package installations, + so that we get it right the first time + :return: + """ + sudo("add-apt-repository ppa:mapnik/boost --yes") + sudo("add-apt-repository ppa:mapnik/v2.2.0 --yes") + sudo("apt-get update --yes") + sudo("apt-get install libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-python-dev \ + libboost-regex-dev libboost-system-dev libboost-thread-dev --yes") + sudo("apt-get build-dep python-imaging --yes") + # create symlinks from the deps to a place where PIL will find them + try: + sudo("ln -f -s /usr/lib/`uname -i`-linux-gnu/libfreetype.so /usr/lib/") + sudo("ln -f -s /usr/lib/`uname -i`-linux-gnu/libjpeg.so /usr/lib/") + sudo("ln -f -s /usr/lib/`uname -i`-linux-gnu/libz.so /usr/lib") + except: + print "symlinks already created... passing" + + # make sure that osgeo is installed + + virtualenv("pip install PIL") + sudo("apt-get install libmapnik mapnik-utils python-mapnik --yes") + # Since we can't install mapnik in the virtualenv, soft link to the installation + try: + sudo('ln -f -s /usr/lib/pymodules/python2.7/mapnik/ {env}/lib/python2.7/site-packages/mapnik'.format(env=env.virtualenv_directory)) + except: + print "link to mapnik installation already present" + + +@task +def install_osgeo(): + sudo('apt-get install python-gdal --yes') + try: + sudo('ln -f -s /usr/lib/python2.7/dist-packages/osgeo {env}/lib/python2.7/site-packages/'.format(env=env.virtualenv_directory)) + except: + print "symlink to osgeo already created" + + +@task +def patch_django_layermapping(): + """ + the layermapping utility in django has a bug that prevents null foreign keys from getting imported + properly. this patch, found at the bug documentation page https://code.djangoproject.com/ticket/17018 + is given a symbolic link to replace the standard django library file, which is renamed to layermapping.py.orig + :return: + """ + sudo('mv {env}/lib/python2.7/site-packages/django/contrib/gis/utils/layermapping.py ' + '{env}/lib/python2.7/site-packages/django/contrib/gis/utils/layermapping.py.orig'.format(env=env.virtualenv_directory)) + sudo('ln -s {PROJ_ROOT}/main/utils/layermapping.py ' + '{env}/lib/python2.7/site-packages/django/contrib/gis/utils/layermapping.py'.format(PROJ_ROOT=PROJ_PATH, + env=env.virtualenv_directory)) + + +@task +def nginx_configure(): + """ + install nginx to proxy all calls in the development environment and static calls in production + """ + + sudo("apt-get install nginx --yes --ignore-missing") + + extension = 'dev' if env.dev else 'prod' + + with cd('/etc/nginx/sites-available'): + sudo('rm -f default') + sudo('rm -f calthorpe.nginx') + sudo('ln -sf {SERVER_ROOT}/conf/etc/nginx/sites-available/calthorpe.nginx.{ext} calthorpe.nginx'.format(SERVER_ROOT=SERVER_ROOT, ext=extension)) + + with cd('../sites-enabled'): + sudo('rm -f default') + sudo('rm -f calthorpe.nginx') + sudo('ln -s ../sites-available/calthorpe.nginx .') + + sudo('/etc/init.d/nginx restart') + + +@task +def create_template_postgis(): + #with settings(warn_only=False): + if not exists(POSTGIS_PATH): + with cd("$HOME"): + run("wget http://download.osgeo.org/postgis/source/postgis-1.5.8.tar.gz") + run("tar xfvz postgis-1.5.8.tar.gz") + with cd("$HOME/postgis-1.5.8"): + run("./configure") + run("make") + sudo("make install") + + run_as_postgres('createdb -E UTF8 template_postgis') + run_as_postgres('psql template_postgis -f {0}/postgis.sql'.format(POSTGIS_PATH)) + run_as_postgres('psql template_postgis -f {0}/spatial_ref_sys.sql'.format(POSTGIS_PATH)) + + +def redis_install(): + sudo('apt-get install redis-server --yes') + # Deal with Redis + with cd('/etc/init.d/'): + if not exists('/etc/init.d/redis', use_sudo=True, verbose=False): + sudo('wget https://raw.github.com/ijonas/dotfiles/master/etc/init.d/redis-server') + sudo('chmod +x /etc/init.d/redis-server') + with cd('/etc/'): + if not exists('/etc/redis.conf', use_sudo=True, verbose=False): + sudo('wget https://raw.github.com/ijonas/dotfiles/master/etc/redis.conf') + sudo('mkdir -p /var/lib/redis') + with settings(warn_only=True): + sudo('useradd --system --home-dir /var/lib/redis redis') + sudo('chown redis.redis /var/lib/redis') + sudo('update-rc.d redis-server defaults') + + #TODO:make sure that $DAEMON matches the install path of redis-server (use: which redis-server to see + # where it's installed)s + + with settings(warn_only=True): + sed('redis-server', r'^DAEMON_ARGS=.*', 'DAEMON_ARGS=/etc/redis/redis.conf', + backup='', use_sudo=True) + sudo('/etc/init.d/redis-server restart') + + +def celery_install(): + #Set up Celery + with cd('/etc/init.d/'): + if not exists('/etc/init.d/celeryevcam', use_sudo=True, verbose=False): + sudo('wget https://raw.github.com/ask/celery/master/contrib/generic-init.d/celeryevcam ' + 'https://raw.github.com/ask/celery/master/contrib/generic-init.d/celeryd ') + sudo('chmod a+x celery*') + sudo('ln -sf {SERVER_ROOT}/conf/celery/etc/default/celeryd /etc/default/celeryd'.format(SERVER_ROOT=SERVER_ROOT)) + sudo('ln -sf {SERVER_ROOT}/conf/celery/etc/default/celeryev /etc/default/celeryev'.format(SERVER_ROOT=SERVER_ROOT)) + sudo('touch /var/log/celeryev.log ') + sudo('chown urbanfootprint:www-data /var/log/celeryev.log') + + +def setup_DB(): + sudo('add-apt-repository ppa:ubuntugis/ubuntugis-unstable --yes') + sudo('apt-get install postgresql postgresql-9.1-postgis postgresql-contrib postgresql-server-dev-9.1 \ + postgresql-client postgresql-client-9.1 postgresql-client-common postgresql-contrib-9.1 postgresql-doc \ + postgresql-doc-9.1 --yes') + with settings(warn_only=True): + sudo('''createuser -sdr urbanfootprint''', user='postgres') + with settings(warn_only=True): + sed('/etc/postgresql/9.1/main/postgresl.conf', + r"#.*listen_addresses='localhost'", + "listen_addresses='*'", + backup='', use_sudo=True) + sudo('apt-get install libgeos-dev libgdal1-dev proj --yes') + + create_template_postgis() + + run('createdb urbanfootprint --template template_postgis') + + #Install dblink module + run('psql urbanfootprint -c "CREATE EXTENSION dblink"') + + # TODO: edit or replace /etc/postgresql/9.1/main/postgresql.conf + # this doesn't yet work from the command line... + + # TODO: why do we do this? --eb + run('''psql urbanfootprint -c "alter user urbanfootprint password 'uf';" ''') + + sudo('/etc/init.d/postgresql restart') + sudo('/etc/init.d/apache2 restart') + + +def install_postGIS2(): + sudo('apt-get install libgeos-dev') + sudo('apt-get install libgdal1-dev') + sudo('apt-get install proj') + with cd('/tmp'): + sudo('wget http://postgis.refractions.net/download/postgis-2.0.1.tar.gz') + sudo('tar -xzf postgis-2.0.1.tar.gz') + with cd('/tmp/postgis-2.0.1'): + sudo('./configure --with-raster --with-topology') + sudo('make') + sudo('make comments') + sudo('make install') + sudo('make comments-install') + run('psql urbanfootprint -c "create extension postgis";') + run('psql urbanfootprint -c "create extension postgis_topology";') + + +def tilestache_configure(): + """Tilestache runs as an apache2 mod_wsgi process on port 8181 + (we are not currently running tilestache this way, rather, through our Django urls) + """ + with cd('''/etc/apache2'''): + try: + sudo('ln -s {SERVER_ROOT}/conf/tilestache.apache /etc/apache2/sites-available'.format(SERVER_ROOT)) + sudo('ln -s {SERVER_ROOT}/conf/tilestache.apache /etc/apache2/sites-enabled'.format(SERVER_ROOT)) + sudo('rm -f /etc/apache2/sites-enabled/000-default') + except: + pass + sed('/etc/apache2/ports.conf', r"NameVirtualHost *:80", + "NameVirtualHost *:80\nNameVirtualHost *:8080", backup='', use_sudo=True) + sed('/etc/apache2/ports.conf', r"Listen *:80", "Listen *:80\nListen *:8080", + backup='', use_sudo=True) + + +@task +def install_filegdb(upgrade=True): + with cd(TEMP_DIR): + proj_filename = 'proj-%s.tar.gz' % PROJ_VER + geos_filename = 'geos-%s.tar.bz2' % GEOS_VER + + run('wget -c %s' % (FILEGDBAPI_LOCATION,)) + run('wget -c http://download.osgeo.org/proj/%s' % (proj_filename,)) + run('wget -c http://download.osgeo.org/geos/%s' % (geos_filename,)) + + run('tar xzf %s' % (FILEGDBAPI_FILE,)) + run('tar xzf %s' % (proj_filename,)) + run('tar xjf %s' % (geos_filename,)) + + #install FILEGDB API based on current ESRI defaults + sudo('rm -rf /usr/local/FileGDB_API 2>/dev/null') + sudo('mv FileGDB_API /usr/local/FileGDB_API') + #sudo('echo \"/usr/local/FileGDB_API/lib\" > /etc/ld.so.conf.d/filegdb.conf') + + with settings(warn_only=True): + sudo('ln -s /usr/local/FileGDB_API/lib/* /usr/local/lib/') + # For SH and variants (sh, bash, etc...): + #fix start + sudo('ln -s /usr/local/lib/libfgdbunixrtl.so /usr/local/lib/libfgdblinuxrtl.so') + # export LD_LIBRARY_PATH + + sudo('ldconfig') + + with cd('proj-%s' % PROJ_VER): + run('./configure') + if upgrade: + run('make clean') + run('make') + sudo('make install') + sudo('ldconfig') + + with cd('geos-%s' % GEOS_VER): + run('./configure') + if upgrade: + run('make clean') + run('make') + sudo('make install') + sudo('ldconfig') + +# @task +# def install_gdal(upgrade=True): + + # For SH and variants (sh, bash, etc...): + # LD_LIBRARY_PATH=/FileGDB_API/lib:$LD_LIBRARY_PATH + # export LD_LIBRARY_PATH + sudo('ln -sf /usr/local/lib/libfgdbunixrtl.so /usr/local/lib/libfgdblinuxrtl.so') + + with cd(TEMP_DIR): + gdal_filename = 'gdal-%s.tar.gz' % GDAL_VER + run('wget -c http://download.osgeo.org/gdal/%s/%s' % (GDAL_VER, gdal_filename,)) + + run('tar xzf %s' % (gdal_filename,)) + sudo('ln -sf /usr/local/FileGDB_API/lib/* /usr/local/lib/') + with cd('gdal-%s' % GDAL_VER): + with shell_env(LD_LIBARY_PATH='/usr/local/FileGDB_API/lib'): + sudo('ldconfig') + run('./configure --with-python --with-fgdb=/usr/local/FileGDB_API') + if upgrade: + run('make clean') + run('make') + sudo('make install') + sudo('ldconfig') + + +@task +def setup_personal_dev_environment(): + """ + takes a default VM setup and configures it to a particular user + """ + + user_name = prompt("please enter your git username (as appears in the VCS): ") + email = prompt("please enter your email address: ") + vm_name = prompt("please enter a new name for this VM: ") + + sudo('su {0} -c "git config --global user.email \'{1}\'"'.format(env.user, email)) + sudo('su {0} -c "git config --global user.name \'{1}\'"'.format(env.user, user_name)) + + sudo('hostname \'{0}\''.format(vm_name)) + + sed('/etc/hosts', "defaultVM", vm_name, use_sudo=True) + + sed('/etc/hostname', 'defaultVM', vm_name, use_sudo=True) + + diff --git a/fabfile/management.py b/fabfile/management.py new file mode 100644 index 000000000..ee0c24d7d --- /dev/null +++ b/fabfile/management.py @@ -0,0 +1,395 @@ +# UrbanFootprint-California, Scenario Planning Model +# +# Copyright (C) 2012-2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Calthorpe Associates (urbanfootprint@calthorpe.com) +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + + +import fileinput +import glob +import os +import re +import sys +import shutil +from cuisine_postgresql import postgresql_role_ensure, postgresql_database_ensure, postgresql_database_check, run_as_postgres +import datetime +from fabric.context_managers import cd, settings +from fabric.contrib.console import confirm +from fabric.decorators import task +from fabric.operations import sudo, local, run +from fabric.state import env +from fabfile.installation import PROJ_ROOT, SERVER_ROOT, manage_py, virtualenv, setup_databases, set_paths, directory_permissions +import logging +logger = logging.getLogger(__name__) +__author__ = 'calthorpe_associates' + + +from fabric.contrib import django + +django.settings_module('footprint.settings') + +from django.conf import settings as django_settings + + +@task +def test_sproutcore_nobuild(): + with cd('/etc/nginx/sites-available'): + sudo('rm -f default') + sudo('rm -f calthorpe.nginx') + sudo('ln -s {SERVER_ROOT}/conf/etc/nginx/sites-available/calthorpe.nginx.dev calthorpe.nginx'.format(SERVER_ROOT=SERVER_ROOT)) + sudo('service nginx restart') + + +@task +def test_sproutcore_build(upgrade_env=True): + build_sproutcore(upgrade_env) + + # sets nginx configuration to point to the scbuild configuration + + with cd('/etc/nginx/sites-available'): + sudo('rm -f default') + sudo('rm -f calthorpe.nginx') + sudo('ln -s {SERVER_ROOT}/conf/etc/nginx/sites-available/calthorpe.nginx.dev.scbuild calthorpe.nginx'.format(SERVER_ROOT=SERVER_ROOT)) + sudo('service nginx restart') + + +def drop_databases(): + """ + Drop the databases. This is not part of the normal setup process, rather it's used to recreate the database in development. + :return: + """ + + sudo('service postgresql restart') + for database_name in ['test_urbanfootprint', 'urbanfootprint']: + if postgresql_database_check(database_name): + # Drop the database if it exists + cmd = 'dropdb -U postgres {database_name}'.format( + database_name=database_name, + ) + run_as_postgres(cmd) + + +@task +def switch_to_prod(reverse=False): + extension = 'dev' if reverse else 'prod' + try: + sudo('rm /etc/supervisor/conf.d/calthorpe.conf') + sudo('rm /etc/nginx/sites-available/calthorpe.nginx') + + except: + pass + + sudo('ln -sf {server_root}/conf/etc/nginx/sites-available/calthorpe.nginx.{nginx} ' + '/etc/nginx/sites-available/calthorpe.nginx '.format(server_root=SERVER_ROOT, nginx=extension)) + sudo('ln -sf {server_root}/conf/etc/supervisor/conf.d/calthorpe.supervisor.{extension} ' + ' /etc/supervisor/conf.d/calthorpe.conf'.format(server_root=SERVER_ROOT, extension=extension)) + sudo('service nginx reload') + sudo('service nginx restart') + sudo('supervisorctl stop all') + sudo('supervisorctl reload') + sudo('supervisorctl start all') + + +@task +def restart_celery(): + sudo('supervisorctl restart celery_worker') + sudo('supervisorctl restart celerybeat') + + +@task +def update_published_data(): + manage_py('footprint_init --skip --results --tilestache') + + +@task +def recreate_django(): + """ + Like recreate_dev but leaves the data tables in place while wiping out all the Django tables + Make sure complete migration scripts exist prior to running this + :return: + """ + if '127.0.0.1' not in env.hosts and not getattr(env, 'allow_remote_recreate', False): + raise Exception("recreate_dev is not allowed for non-localhosts for security purposes") + + sudo('service postgresql restart') + + manage_py('footprint_init --skip --relayer_selection') + + database_name = 'urbanfootprint' + cmd = 'pg_dump -U postgres -Fc {database_name} -n public > /tmp/footprint_public.dump'.format( + database_name=database_name, + ) + run_as_postgres(cmd) + + cmd = 'pg_dump -U postgres -Fc {database_name} -N public > /tmp/footprint_schemas.dump'.format( + database_name=database_name, + ) + run_as_postgres(cmd) + + cmd = 'dropdb -U postgres {database_name}'.format( + database_name=database_name, + ) + run_as_postgres(cmd) + + postgresql_role_ensure('calthorpe', '[PASSWORD]', superuser=True, createdb=True, createrole=True, + inherit=True, login=True) + postgresql_database_ensure('urbanfootprint', owner='calthorpe', template='template_postgis') + + with cd(PROJ_ROOT): + manage_py('syncdb --noinput') + manage_py('migrate') + + for public_import_table in map( + lambda name: 'main_%s' % name, + ['geography', 'medium', 'template', 'buildingattributes', 'buildingattributeset', 'builtform', 'placetype', + 'placetypecomponentcategory', 'placetypecomponent', 'primarycomponent', 'sacoglandusedefinition', 'sacoglanduse', 'scaglandusedefinition', 'scaglanduse', 'sutter*']): + cmd = 'pg_restore -U postgres -d {database_name} --data-only -t {public_import_table} /tmp/footprint_public.dump'.format( + database_name=database_name, + public_import_table=public_import_table + ) + run_as_postgres(cmd) + + cmd = 'pg_restore -U postgres -d {database_name} /tmp/footprint_schemas.dump'.format( + database_name=database_name, + ) + run_as_postgres(cmd) + + python = os.path.join(env.virtualenv_directory, 'bin/python') + output = local(python + ' ' + PROJ_ROOT + '/manage.py sqlsequencereset main', capture=True).replace('BEGIN;\n','').replace('COMMIT;','') + + with settings(warn_only=True): + cmd = 'psql -U postgres -d {database_name} -c "{output}"'.format( + database_name=database_name, + output=output + ) + run_as_postgres(cmd) + with cd(PROJ_ROOT): + manage_py('collectstatic --noinput') + manage_py('footprint_init') + + clear_tilestache_cache() + + +@task +def recreate_dev(init=True): + """ + Drops and recreates the databases for development, then initializes main + This will raise an error if 127.0.0.1 is not in env.hosts to protect live databases + Make sure complete migration scripts exist prior to running this + :return: + """ + + if '127.0.0.1' not in env.hosts and not getattr(env, 'allow_remote_recreate', False): + raise Exception("recreate_dev is not allowed for non-localhosts for security purposes") + if not getattr(env, 'allow_remote_recreate', False): + if not confirm("This command destroys the database and regenerates it -- proceed?", default=False): + return + if not django_settings.USE_SAMPLE_DATA_SETS: + if not confirm("THIS IS A PRODUCTION DATA SET! REALLY DELETE IT?", default=False): + return + + drop_databases() + setup_databases() + + with cd(PROJ_ROOT): + sudo('find . -name \'*.pyc\' -delete') + manage_py('syncdb --noinput') + manage_py('migrate') + manage_py('collectstatic --noinput') + + if init is True: + manage_py('footprint_init') + + clear_tilestache_cache() + + +@task +def clear_tilestache_cache(): + sudo('rm -rf /tmp/stache/*') + sudo('chown -R calthorpe:www-data /tmp/stache') + sudo('chmod -R ug+w /tmp/stache') + + +@task +def delete_all_cartocss(): + sudo("rm -r /srv/calthorpe_media/cartocss/*") + sudo("rm -r /srv/calthorpe_static/cartocss/*") + + +@task +def build_sproutcore(upgrade_env=True): + with cd(SERVER_ROOT): + sudo('/srv/calthorpe_env/bin/fab localhost update_sproutcore_build_number') + + # build sproutcore + with cd('{root}/sproutcore'.format(root=SERVER_ROOT)): + sudo('rm -rf builds') + # Build main in the build dir + sudo('sproutcore build fp --buildroot=builds --dont_minify', user=env.deploy_user) + # Change ownership on output + sudo('chown -R {0}.www-data ./builds'.format(env.deploy_user)) + + # ln to the builds dir from Django's static dir + sudo('ln -f -s {root}/sproutcore/builds/static/* {root}/footprint/main/static'.format(root=SERVER_ROOT), user=env.deploy_user) + + sudo('ln -f -s {root}/sproutcore/builds/static/fp/en/0.1/index.html ' + '{root}/footprint/main/templates/footprint/index.html'.format(root=SERVER_ROOT), user=env.deploy_user) + + # do a collect static to grab all static files and link them to the right directory + manage_py('collectstatic -l --noinput') + +@task +def deploy(upgrade_env=True): + """ + Deploy code, pip dependencies and execute migrations + """ + + set_paths() + directory_permissions() + with cd(PROJ_ROOT): + sudo('find . -name \'*.pyc\' -delete') + sudo('git pull', user=env.deploy_user) + + #with cd('/srv/calthorpe/urbanfootprint'): + #with settings(warn_only=True): + # sudo('su {0} -c "git rm -r --cached ."'.format(env.deploy_user)) + # sudo('su {0} -c "rm -rf /srv/calthorpe/urbanfootprint/calthorpe/server/calthorpe/main/sproutcore/frameworks/sc-table"'.format(env.deploy_user)) + # sudo('su {0} -c "git submodule add https://github.com/jslewis/sctable.git /srv/calthorpe/urbanfootprint/calthorpe/server/calthorpe/main/sproutcore/frameworks/sc-table"'.format(env.deploy_user)) + #sudo('su {0} -c "git submodule init"'.format(env.deploy_user)) + #sudo('su {0} -c "git submodule update"'.format(env.deploy_user)) + + if (upgrade_env == True): + virtualenv('pip install -U -r ' + PROJ_ROOT + '/pip-req.txt') + else: + virtualenv('pip install -r ' + PROJ_ROOT + '/pip-req.txt') + + manage_py('syncdb --noinput') + + # just adding the --delete-ghost-migrations flag until everything runs + manage_py('migrate --delete-ghost-migrations') + + # manage_py('footprint_init') + + build_sproutcore(upgrade_env) + # because of a bug the very first time it is ran where the restart + # won't load the new configuration files + + with settings(warn_only=True): + sudo('supervisorctl stop all') + sudo('/etc/init.d/supervisor stop') + sudo('/etc/init.d/nginx stop') + # sudo('/etc/init.d/varnish stop') + + sudo('/etc/init.d/supervisor start') + sudo('supervisorctl start all') + sudo('/etc/init.d/nginx start') + # sudo('/etc/init.d/varnish start') + + +@task +def update_license(): + def pre_append(line, file_name): + fobj = fileinput.FileInput(file_name, inplace=1) + first_line = fobj.readline() + sys.stdout.write("%s\n%s" % (line, first_line)) + for line in fobj: + sys.stdout.write("%s" % line) + fobj.close() + + boilerplate_file = open('/footprint/LICENSE', 'r') + boilerplate = boilerplate_file.read() + boilerplate_file.close() + + def sub_py(boilerplate): + text = re.sub(r'^', '# ', boilerplate, 0, re.M) + return re.sub(r'$', '\n', text, 1) + def sub_js(boilerplate): + text = re.sub(r'^', '* ', boilerplate, 0, re.M) + text = re.sub(r'^', '/* \n', text, 1) + return re.sub(r'$', '\n */\n', text, 1) + def sub_html(boilerplate): + text = re.sub(r'^', '\n', text, 1) + + format_dict = { + 'py': sub_py(boilerplate), + 'js': sub_js(boilerplate), + 'css': sub_js(boilerplate), + 'html': sub_html(boilerplate) + } + + files_to_check = collect_file_paths(django_settings.ROOT_PATH, ['py', 'js', 'css', 'html']) + + # remove old license text + for path in files_to_check: + old_license = re.compile(r"\sUrbanFootprint-California.*Web:\swww\.footprint\.com", re.S | re.M) + o = open(path) + data = o.read() + o.close() + if 'manage.py' in path: + continue + new_file_contents = re.sub(old_license, "", data) + if new_file_contents: + file = open(path, 'w') + file.write(new_file_contents) + file.close() + + ext = re.match(r'.*\.(\w+)$', path).group(1) + + if format_dict.get(ext): + print path + pre_append(format_dict[ext], path) + + +def collect_file_paths(root_path, extensions): + files_to_check = [] + for r, d, f in os.walk(root_path): + for ext in extensions: + files_to_check.extend(glob.glob(os.path.join(r, "*.{0}".format(ext)))) + return files_to_check + +@task +def update_sproutcore_build_number(): + print 'updating sc build on ' + str(env.hosts) + build_number_format = "{year}.{month}.{day}".format + + today = datetime.date.today() + + build_number = build_number_format(year=today.year, month=today.month, day=today.day) + print build_number + + sproutcore_directory = os.path.join(django_settings.SERVER_ROOT, 'sproutcore') + + files_to_inspect = collect_file_paths(sproutcore_directory, ['js']) + + old_revision_line = re.compile(r"value:\s'UrbanFootprint\srev\.\s\d{4}\.\d{1,2}\.\d{1,2}\s") + + updated = False + for path in files_to_inspect: + o = open(path) + data = o.read() + o.close() + + file_has_build_number = re.search(old_revision_line, data) + if file_has_build_number: + new_file_contents = re.sub(old_revision_line, "value: \'UrbanFootprint rev. {0} ".format(build_number), data) + print "value: \'UrbanFootprint rev. {0} ".format(build_number) + file = open(path, 'w') + file.write(new_file_contents) + file.close() + print 'Updated build number in ' + path + updated = True + if not updated: + print 'did not update build number!' \ No newline at end of file diff --git a/footprint/__init__.py b/footprint/__init__.py new file mode 100644 index 000000000..7e699adb3 --- /dev/null +++ b/footprint/__init__.py @@ -0,0 +1,13 @@ +import logging +import os +from fabric.operations import sudo +from fabric.state import env +logger = logging.getLogger(__name__) + +# if not os.environ.get('IS_CELERY', None): +# logger.info('restarting celery') +# env.host_string = 'localhost' +# env.user = 'calthorpe' +# env.sudo_user = 'calthorpe' +# env.password = '[PASSWORD]' +# sudo('supervisorctl restart celery_worker', tty=False) \ No newline at end of file diff --git a/footprint/celery.py b/footprint/celery.py new file mode 100644 index 000000000..02049d684 --- /dev/null +++ b/footprint/celery.py @@ -0,0 +1,31 @@ +from __future__ import absolute_import +__author__ = 'calthorpe_associates' + +import os + +from celery import Celery + +# set the default Django settings module for the 'celery' program. +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'footprint.settings') + +app = Celery('footprint', + broker='redis://localhost:6379/0', + backend='redis://localhost:6379/0', + include=['footprint.main.publishing.config_entity_publishing', + 'footprint.main.publishing.data_export_publishing', + 'footprint.main.publishing.data_import_publishing', + 'footprint.main.models.analysis_module']) + +# Using a string here means the worker will not have to +# pickle the object when using Windows. +app.config_from_object('django.conf:settings') +# app.conf.update( +# CELERYD_USER="calthorpe", +# CELERYD_GROUP="www-data" +# ) +# app.autodiscover_tasks(lambda: settings.INSTALLED_APPS) + + +@app.task(bind=True) +def debug_task(self): + print('Request: {0!r}'.format(self.request)) \ No newline at end of file diff --git a/footprint/client/__init__.py b/footprint/client/__init__.py new file mode 100644 index 000000000..bed379f77 --- /dev/null +++ b/footprint/client/__init__.py @@ -0,0 +1,20 @@ + + + +from footprint.client.configuration.sacog.built_form.sacog_land_use import SacogLandUse +from footprint.client.configuration.sacog.built_form.sacog_land_use_definition import SacogLandUseDefinition +from footprint.client.configuration.sacog.base.sacog_existing_land_use_parcel_feature \ + import SacogExistingLandUseParcelFeature +from footprint.client.configuration.sacog.base.sacog_hardwood_feature import SacogHardwoodFeature +from footprint.client.configuration.sacog.base.sacog_stream_feature import SacogStreamFeature +from footprint.client.configuration.sacog.base.sacog_vernal_pool_feature import SacogVernalPoolFeature +from footprint.client.configuration.sacog.base.sacog_wetland_feature import SacogWetlandFeature + +from footprint.client.configuration.sacog.base.sacog_light_rail_feature import SacogLightRailFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_feature import SacogLightRailStopsFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_one_mile_feature \ + import SacogLightRailStopsOneMileFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_half_mile_feature \ + import SacogLightRailStopsHalfMileFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_quarter_mile_feature \ + import SacogLightRailStopsQuarterMileFeature diff --git a/footprint/client/configuration/__init__.py b/footprint/client/configuration/__init__.py new file mode 100644 index 000000000..38a28efe6 --- /dev/null +++ b/footprint/client/configuration/__init__.py @@ -0,0 +1,16 @@ +__author__ = 'calthorpe' + +# Import all client models that have static tables so that we have a single migration path +from footprint.client.configuration.fixture import InitFixture +from footprint.client.configuration.utils import resolve_fixture, resolve_client_module +from footprint import settings + +# Load all client modules into the system, even though we only will configure one CLIENT +# This forces South to create all client specific table definitions +for client in settings.ALL_CLIENTS: + client_init = resolve_fixture(None, "init", InitFixture, client) + #client_init.import_database() + for module_tuple in client_init.model_class_modules(): + # Load the module so that Django and South find the classes + resolve_client_module(module_tuple[0], module_tuple[1], client) + diff --git a/footprint/client/configuration/default/__init__.py b/footprint/client/configuration/default/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/default/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/default/built_form/__init__.py b/footprint/client/configuration/default/built_form/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/default/built_form/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/default/built_form/default_built_form.py b/footprint/client/configuration/default/built_form/default_built_form.py new file mode 100644 index 000000000..a9d82d4b3 --- /dev/null +++ b/footprint/client/configuration/default/built_form/default_built_form.py @@ -0,0 +1,277 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import itertools +import csv + +from django.db.models.signals import post_save +from django.template.defaultfilters import slugify + +from footprint.main.initialization.built_form.built_form_importer import BuiltFormImporter +from footprint.client.configuration.default.default_mixin import DefaultMixin +from footprint.main.lib.functions import map_to_dict +from footprint.main.models import BuildingUsePercent, PlacetypeComponentPercent, PrimaryComponent, \ + PrimaryComponentPercent, Placetype, BuildingAttributeSet, PlacetypeComponent, GlobalConfig +from footprint.client.configuration.fixture import BuiltFormFixture +from footprint.main.models.built_form.placetype_component import PlacetypeComponentCategory +from footprint.main.models.tag import Tag +from footprint.main.tests.test_data.sample_built_form_sets import load_placetype_csv +from footprint import settings +from django.conf import settings + + +__author__ = 'calthorpe' + +import logging +# Get an instance of a logger +logger = logging.getLogger(__name__) + +# By default we create a BuiltFormSet for each class, one for Placetypes, BuildingTypes, and Buildings. +# Sets can contain instances of multiple classes, but this is the configuration is easiest to understand +class DefaultBuiltFormFixture(DefaultMixin, BuiltFormFixture): + + def update_or_create_building_attributes(self, built_form, building_attributes_dict, built_form_created): + if built_form_created: + building_attributes = BuildingAttributeSet.objects.create(**building_attributes_dict) + built_form.building_attributes = building_attributes + built_form.save() + else: + building_attributes = built_form.building_attributes + for (key, value) in building_attributes_dict.items(): + setattr(building_attributes, key, value) + built_form.save() + + def built_forms(self, client='default'): + """ + Returns an unpersisted dict with lists placetypes, buildingtypes, buildings, etc. fixtures + :param default_built_forms: + :return: + """ + + # Create the definitions for all default built_forms in the system. These depend on one another and are + # made into actual instances in persist_built_forms + if not isinstance(self.config_entity, GlobalConfig) or settings.SKIP_ALL_BUILT_FORMS: + return { + 'placetypes': [], + 'placeptype_components': [], + 'primary_components': [], + 'primary_component_percents': [], + 'placetype_component_percents': [], + 'building_use_percents': [], + } + + built_forms_dict = BuiltFormImporter().construct_built_forms(client) + built_forms = [] + + from footprint.main.models.built_form.primary_component import on_instance_modify as on_primary_component_modify + + post_save.disconnect(on_primary_component_modify, sender=PrimaryComponent) + logger.info("Beginning Primary Components") + for primary_component_dict in built_forms_dict['primary_components']: + name = primary_component_dict['building_attributes'].pop('name', None) + primary_component, created, updated = PrimaryComponent.objects.update_or_create( + key='b__' + slugify(name).replace('-', '_'), + defaults=dict( + name=name, + ) + ) + self.update_or_create_building_attributes(primary_component, primary_component_dict['building_attributes'], created) + + built_forms.append(primary_component) + + logger.info("Beginning Building Use Percents") + for building_use_percent_dict in built_forms_dict['building_use_percents']: + built_form = PrimaryComponent.objects.get(name=building_use_percent_dict['built_form_name']) + use_attributes = building_use_percent_dict['built_form_uses'] + definition = use_attributes.pop('building_use_definition') + use_percent, created, updated = BuildingUsePercent.objects.update_or_create( + building_attributes=built_form.building_attributes, + building_use_definition=definition, + defaults=use_attributes + ) + pass + + if not settings.TEST_SKIP_BUILT_FORM_COMPUTATIONS: + logger.info("Calculating Primary Components") + for primary_component in PrimaryComponent.objects.all(): + if not settings.TEST_SKIP_BUILT_FORM_COMPUTATIONS: + primary_component.building_attributes.calculate_derived_fields() + post_save.connect(on_primary_component_modify, sender=PrimaryComponent) + + from footprint.main.models.built_form.placetype_component import on_instance_modify as on_placetype_component_modify + + post_save.disconnect(on_placetype_component_modify, sender=PlacetypeComponent) + + logger.info("Beginning Placetype Components") + for placetype_component_dict in built_forms_dict['placetype_components']: + name = placetype_component_dict['name'] + category = placetype_component_dict['component_category'] + color = placetype_component_dict.get('color', "#909090") + component_category = PlacetypeComponentCategory.objects.get_or_create(name=category)[0] + placetype_component, created, updated = PlacetypeComponent.objects.update_or_create( + key='bt__' + slugify(name).replace('-', '_'), + defaults=dict( + name=name, + component_category=component_category + ) + ) + self.update_or_create_building_attributes(placetype_component, {}, created) + + placetype_component.create_built_form_medium(color) + + placetype_component.save() + built_forms.append(placetype_component) + + logger.info("Beginning Primary Component Percents") + for primary_component_percent in built_forms_dict['primary_component_percents']: + + placetype_component_dict = PlacetypeComponent.objects.get( + name=primary_component_percent['placetype_component_name']) + if primary_component_percent['percent'] > 0: + PrimaryComponentPercent.objects.update_or_create( + primary_component=PrimaryComponent.objects.get( + name=primary_component_percent['primary_component_name'] + ), + placetype_component=placetype_component_dict, + defaults=dict(percent=primary_component_percent['percent']) + ) + if not settings.TEST_SKIP_BUILT_FORM_COMPUTATIONS: + placetype_component_dict.aggregate_built_form_attributes() + + placetype_color_lookup = map_to_dict( + lambda placetype: [ + placetype.name, + placetype.color], + load_placetype_csv()) + + examples_file = 'placetype_example_areas.csv' + # # Read in placetype examples and create a dictionary so you + bf_examples_path = '%s/sproutcore/apps/fp/resources/Text/%s' % (settings.PROJECT_ROOT, examples_file) + reader = csv.DictReader(open(bf_examples_path, "rU")) + + #This dictionary has builtform id's as the key, and the value is an array of dictionaries, each containing + #data about an example area + bf_examples = {} + + for row in reader: + if row: + pt__key = row["pt__key"] + if bf_examples.get(pt__key): + bf_examples[pt__key].append(row) + else: + bf_examples[pt__key] = [row] + + + logger.info("Beginning Placetypes") + for placetype_dict in built_forms_dict['placetypes']: + name = placetype_dict['building_attributes'].pop('name', None) + + placetype, created, updated = Placetype.objects.update_or_create( + key='pt__' + slugify(name).replace('-', '_'), + defaults=dict( + name=name, + intersection_density=placetype_dict['intersection_density']) + ) + building_attributes_dict = placetype_dict['building_attributes'] + self.update_or_create_building_attributes(placetype, building_attributes_dict, created) + placetype.create_built_form_medium(placetype_color_lookup.get(placetype.name, "#909090")) + placetype.update_or_create_built_form_media() + placetype.update_or_create_built_form_examples(bf_examples.get(placetype.key) if bf_examples.get(placetype.key) else []) + + built_forms.append(placetype) + + placetype.save() + + placetype_dict = built_forms_dict['placetype_component_percents'].get(placetype.name, None) + if not placetype_dict: + logger.warning("Expected built_forms_dict['placetype_component_percents'] to have key %s", + placetype.name) + else: + for placetype_component_dict, attributes in placetype_dict.items(): + component = PlacetypeComponent.objects.get(name=placetype_component_dict) + if attributes['percent'] > 0: + PlacetypeComponentPercent.objects.update_or_create( + placetype=placetype, + placetype_component=component, + defaults=dict(percent=attributes['percent'])) + + post_save.connect(on_placetype_component_modify, sender=PlacetypeComponent) + + if not settings.TEST_SKIP_BUILT_FORM_COMPUTATIONS: + logger.info("Computing Placetypes") + for placetype in Placetype.objects.all(): + placetype.aggregate_built_form_attributes() + + built_forms_dict = { + 'placetypes': Placetype.objects.all(), + 'placetype_components': PlacetypeComponent.objects.all(), + 'primary_components': PrimaryComponent.objects.all(), + + 'primary_component_percents': PrimaryComponentPercent.objects.all(), + 'building_use_percents': BuildingUsePercent.objects.all(), + 'placetype_component_percents': PlacetypeComponentPercent.objects.all() + } + + return built_forms_dict + + def tag_built_forms(self, built_forms_dict, client='default'): + """ + Tag BuiltForms based on their character + :return: + """ + if settings.SKIP_ALL_BUILT_FORMS: + return + + built_form_importer = BuiltFormImporter() + + for placetype_component in built_forms_dict.get('placetype_components', []): + placetype_component.tags.add( + Tag.objects.update_or_create(tag=placetype_component.component_category.name)[0]) + + for built_form in itertools.chain(built_forms_dict.get('placetypes', []), + built_forms_dict.get('primary_components', [])): + tag, created, updated = Tag.objects.update_or_create(tag='Unsorted') + if len(built_form.tags.filter(tag=tag.tag)) == 0: + built_form.tags.add(tag) + + def built_form_sets(self): + return self.matching_scope([ + dict( + scope=GlobalConfig, + key='uf_placetype', + name='UF Placetypes', + description='BuiltFormSet containing only placetypes', + clazz=Placetype, + client='default' + ), + dict( + scope=GlobalConfig, + key='uf_placetype_components', + name='UF BuildingTypes', + description='BuiltFormSet containing only buildingtypes', + clazz=PlacetypeComponent, + client='default' + ), + dict( + scope=GlobalConfig, + key='uf_primary_components', + name='Buildings', + description='BuiltFormSet containing only buildings', + clazz=PrimaryComponent, + client='default' + ) + ], class_scope=self.config_entity and self.config_entity.__class__) diff --git a/footprint/client/configuration/default/built_form/default_import_placetype.py b/footprint/client/configuration/default/built_form/default_import_placetype.py new file mode 100644 index 000000000..8298be202 --- /dev/null +++ b/footprint/client/configuration/default/built_form/default_import_placetype.py @@ -0,0 +1,24 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from csvImporter.fields import CharField, FloatField +from csvImporter.model import CsvModel + +class ImportedPlacetype(CsvModel): + name = CharField(prepare=lambda x: x or '') + clean_name = CharField(prepare=lambda x: x or '') + color = CharField(prepare=lambda x: x or '') + intersection_density = FloatField(prepare=lambda x: x or 0) + + class Meta: + delimiter = "," + has_header = True diff --git a/footprint/client/configuration/default/built_form/default_import_placetype_component.py b/footprint/client/configuration/default/built_form/default_import_placetype_component.py new file mode 100644 index 000000000..0e49de91f --- /dev/null +++ b/footprint/client/configuration/default/built_form/default_import_placetype_component.py @@ -0,0 +1,61 @@ +__author__ = 'calthorpe' + +from csvImporter.fields import CharField, FloatField, IntegerField +from csvImporter.model import CsvModel + +__author__ = 'calthorpe' + +class ImportPlacetypeComponent(CsvModel): +# BTID,Building_Type, +# Urban Mixed Use,Urban Residential,Urban Commercial,City Mixed Use,City Residential,City Commercial, +# Town Mixed Use,Town Residential,Town Commercial,Village Mixed Use,Village Residential,Village Commercial, +# Neighborhood Residential,Neighborhood Low,Office Focus,Mixed Office and R&D,Office/Industrial,Industrial Focus, +# Low-Density Employment Park,High Intensity Activity Center,Mid Intensity Activity Center, +# Low Intensity Retail-Centered N'Hood,Retail: Strip Mall/ Big Box,Industrial/Office/Res Mixed High, +# Industrial/Office/Res Mixed Low,Suburban Multifamily,Suburban Mixed Residential,Residential Subdivision, +# Large Lot Residential Area,Rural Residential,Rural Ranchettes,Rural Employment,Campus/ University, +# Institutional,Parks & Open Space,BuildingType Name,Gross_Net_Flag + category = CharField(prepare=lambda x: x or '') + btid = IntegerField(prepare=lambda x: x or 0) + color = CharField(prepare=lambda x: x or '') + name = CharField(prepare=lambda x: x or '') + urban_mixed_use = FloatField(prepare=lambda x: x or 0) + urban_residential = FloatField(prepare=lambda x: x or 0) + urban_commercial = FloatField(prepare=lambda x: x or 0) + city_mixed_use = FloatField(prepare=lambda x: x or 0) + city_residential = FloatField(prepare=lambda x: x or 0) + city_commercial = FloatField(prepare=lambda x: x or 0) + town_mixed_use = FloatField(prepare=lambda x: x or 0) + town_residential = FloatField(prepare=lambda x: x or 0) + town_commercial = FloatField(prepare=lambda x: x or 0) + village_mixed_use = FloatField(prepare=lambda x: x or 0) + village_residential = FloatField(prepare=lambda x: x or 0) + village_commercial = FloatField(prepare=lambda x: x or 0) + neighborhood_residential = FloatField(prepare=lambda x: x or 0) + neighborhood_low = FloatField(prepare=lambda x: x or 0) + office_focus = FloatField(prepare=lambda x: x or 0) + mixed_office_and_r_and_d = FloatField(prepare=lambda x: x or 0) + office_industrial = FloatField(prepare=lambda x: x or 0) + industrial_focus = FloatField(prepare=lambda x: x or 0) + low_density_employment_park = FloatField(prepare=lambda x: x or 0) + high_intensity_activity_center = FloatField(prepare=lambda x: x or 0) + mid_intensity_activity_center = FloatField(prepare=lambda x: x or 0) + low_intensity_retail_centered_neighborhood = FloatField(prepare=lambda x: x or 0) + retail_strip_mall_big_box = FloatField(prepare=lambda x: x or 0) + industrial_office_residential_mixed_high = FloatField(prepare=lambda x: x or 0) + industrial_office_residential_mixed_low = FloatField(prepare=lambda x: x or 0) + suburban_multifamily = FloatField(prepare=lambda x: x or 0) + suburban_mixed_residential = FloatField(prepare=lambda x: x or 0) + residential_subdivision = FloatField(prepare=lambda x: x or 0) + large_lot_residential = FloatField(prepare=lambda x: x or 0) + rural_residential = FloatField(prepare=lambda x: x or 0) + rural_ranchettes = FloatField(prepare=lambda x: x or 0) + rural_employment = FloatField(prepare=lambda x: x or 0) + campus_or_university = FloatField(prepare=lambda x: x or 0) + institutional = FloatField(prepare=lambda x: x or 0) + parks_and_open_space = FloatField(prepare=lambda x: x or 0) + + class Meta: + delimiter = "," + has_header = True + diff --git a/footprint/client/configuration/default/built_form/default_import_primary_component.py b/footprint/client/configuration/default/built_form/default_import_primary_component.py new file mode 100644 index 000000000..662587a4f --- /dev/null +++ b/footprint/client/configuration/default/built_form/default_import_primary_component.py @@ -0,0 +1,110 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from csvImporter.fields import CharField, FloatField, IntegerField +from csvImporter.model import CsvModel + +__author__ = 'calthorpe' + +class ImportPrimaryComponent(CsvModel): + # id + id = IntegerField(prepare=lambda x: x or 0) + #source,hyperlink,building_type, + source = CharField(prepare=lambda x: x or '') + hyperlink = CharField(prepare=lambda x: x or '') + placetype_component = CharField(prepare=lambda x: x or '') + #building,household_size, + name = CharField(prepare=lambda x: x or '') + + vacancy_rate = FloatField(prepare=lambda x: x or 0) + household_size = FloatField(prepare=lambda x: x or 0) + + #all_uses, + all_uses = CharField(prepare=lambda x: x or '') + du_density = CharField(prepare=lambda x: x or '') + emp_density = CharField(prepare=lambda x: x or '') + +#Pct_SF_Large_Lot,Pct_SF_Small_Lot,Pct_Attached_SF,Pct_MF_2_to_4,Pct_MF_5_Plus, + percent_single_family_large_lot = FloatField(prepare=lambda x: x or 0) + percent_single_family_small_lot = FloatField(prepare=lambda x: x or 0) + percent_attached_single_family = FloatField(prepare=lambda x: x or 0) + percent_multifamily_2_to_4 = FloatField(prepare=lambda x: x or 0) + percent_multifamily_5_plus = FloatField(prepare=lambda x: x or 0) + +# Pct_Emp_Office_Svc,Pct_Educ_Svc,Pct_Medical_Svc,Pct_Public_Admin, + percent_office_services = FloatField(prepare=lambda x: x or 0) + percent_education_services = FloatField(prepare=lambda x: x or 0) + percent_medical_services = FloatField(prepare=lambda x: x or 0) + percent_public_admin = FloatField(prepare=lambda x: x or 0) + +#Pct_Retail_Svc,Pct_Restuarant,Pct_Accommodation,Pct_Arts_Entertainment,Pct_Other_Svc, + percent_retail_services = FloatField(prepare=lambda x: x or 0) + percent_restaurant = FloatField(prepare=lambda x: x or 0) + percent_accommodation = FloatField(prepare=lambda x: x or 0) + percent_arts_entertainment = FloatField(prepare=lambda x: x or 0) + percent_other_services = FloatField(prepare=lambda x: x or 0) + +# Pct_Manufacturing,Pct_Transport_warehouse,Pct_Wholesale,Pct_Construction_Util,Pct_Agriculture,Pct_Extraction, + percent_manufacturing = FloatField(prepare=lambda x: x or 0) + percent_transport_warehouse = FloatField(prepare=lambda x: x or 0) + percent_wholesale = FloatField(prepare=lambda x: x or 0) + percent_construction_utilities = FloatField(prepare=lambda x: x or 0) + + percent_agriculture = FloatField(prepare=lambda x: x or 0) + percent_extraction = FloatField(prepare=lambda x: x or 0) + +# percent_of_building_type,floors,percent_residential,percent_retail,percent_office,percent_industrial, + percent_of_placetype_component = FloatField(prepare=lambda x: x or 0) + floors = FloatField(prepare=lambda x: x or 0) + +#total_far,parking_spaces,parking_structure_square_feet,residential_efficiency,residential_lot_square_feet,square_feet_per_du, + total_far = FloatField(prepare=lambda x: x or 0) + parking_spaces = FloatField(prepare=lambda x: x or 0) + parking_structure_square_feet = FloatField(prepare=lambda x: x or 0) + + residential_efficiency = FloatField(prepare=lambda x: x or 0) + residential_average_lot_size = FloatField(prepare=lambda x: x or 0) + residential_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + +# retail_efficiency,retail_square_feet_per_employee,office_efficiency,office_square_feet_per_employee, + retail_efficiency = FloatField(prepare=lambda x: x or 0) + retail_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + office_efficiency = FloatField(prepare=lambda x: x or 0) + office_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + +#industrial_efficiency,industrial_square_feet_per_employee, + industrial_efficiency = FloatField(prepare=lambda x: x or 0) + industrial_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + +# misc_percent_of_retail_use,retail_percent_of_retail_use,restaurant_percent_of_retail_use,grocery_percent_of_retail_use, + misc_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + retail_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + restaurant_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + grocery_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + +# hardscape_percent,irrigated_percent,impervious_roof_percent,impervious_hardscape_percent,pervious_hardscape_percent,softscape_landscape_percent, + hardscape_percent = FloatField(prepare=lambda x: x or 0) + irrigated_percent = FloatField(prepare=lambda x: x or 0) + impervious_roof_percent = FloatField(prepare=lambda x: x or 0) + impervious_hardscape_percent = FloatField(prepare=lambda x: x or 0) + pervious_hardscape_percent = FloatField(prepare=lambda x: x or 0) + softscape_and_landscape_percent = FloatField(prepare=lambda x: x or 0) + + id2 = CharField(prepare=lambda x: x or 0) + class Meta: + delimiter = "," + has_header = True diff --git a/footprint/client/configuration/default/built_form/import_csv/buildings.csv b/footprint/client/configuration/default/built_form/import_csv/buildings.csv new file mode 100644 index 000000000..375e88f59 --- /dev/null +++ b/footprint/client/configuration/default/built_form/import_csv/buildings.csv @@ -0,0 +1,245 @@ +"id","source","hyperlink","building_type","building","vacancy_rate","household_size","all_uses","du_density","emp_density","Pct_SF_Large_Lot","Pct_SF_Small_Lot","Pct_Attached_SF","Pct_MF_2_to_4","Pct_MF_5_Plus","Pct_Emp_Office_Svc","Pct_Educ_Svc","Pct_Medical_Svc","Pct_Public_Admin","Pct_Retail_Svc","Pct_Restaurant","Pct_Accommodation","Pct_Arts_Entertainment","Pct_Other_Svc","Pct_Manufacturing","Pct_Transport_warehouse","Pct_Wholesale","Pct_Construction_Util","Pct_Agriculture","Pct_Extraction","percent_of_building_type","floors","total_far","parking_spaces","parking_structure_square_feet","residential_efficiency","residential_lot_square_feet","square_feet_per_du","retail_efficiency","retail_square_feet_per_employee","office_efficiency","office_square_feet_per_employee","industrial_efficiency","industrial_square_feet_per_employee","misc_percent_of_retail_use","retail_percent_of_retail_use","restaurant_percent_of_retail_use","grocery_percent_of_retail_use","hardscape_percent","irrigated_percent","impervious_roof_percent","impervious_hardscape_percent","pervious_hardscape_percent","softscape_landscape_percent","id" +1,0,0,"Skyscraper Mixed Use","181 Fremont Street",0.0635,1.605,0,330.5447432,5100.405552,0,0,0,0,0.33,0.67,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3,66,51.4,677,223410,0.85,43560,1900,0.85,475,0.85,250,0.85,1000,0.52,0.34,0.07,0.07,0.99,0.7,0.99,0,0,0,11 +2,0,0,"Skyscraper Mixed Use","Encinal Tower (Oakland, CA)",0.0635,1.915,0,112.0933382,1044.968373,0,0,0,0,0.45,0.5,0,0,0,0.0205,0.0035,0,0.00182,0.02418,0,0,0,0,0,0,0.3,56,18.4,543,179190,0.85,175,2735,0.85,475,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.99,0.7,0.99,0,0,0,12 +3,0,"http://casestudies.uli.org/Profile.aspx?j=8218&p=5&c=4, http://www.som.com/content.cfm/john_hancock_center, http://www.johnhancockcenterchicago.com/Index3.htm","Skyscraper Mixed Use","Skyscraper Mixed (John Hancock Center, Chicago)",0.0635,1.815,0,305.3220923,1457.774208,0,0,0,0,0.6,0.34,0,0,0,0.0246,0.0042,0,0.002184,0.029016,0,0,0,0,0,0,0.4,100,26.8,295,97350,0.85,86,1950,0.85,550,0.85,250,0.85,1000,0.52,0.34,0.07,0.07,0.99,0.5,0.99,0,0,0,13 +6,0,0,"High-Rise Mixed Use","High-Rise Residential (Visionaire, New York)",0.0635,2.065,0,376.7895093,168.432,0,0,0,0,0.92,0.07,0,0,0,0.0041,0.0007,0,0.000364,0.004836,0,0,0,0,0,0,0.05,35,15.2,135.5,44715,0.72,0,1164,1,475,1,300,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,26 +9,0,0,"High-Rise Mixed Use","High-Rise Mixed (201 Folsom, San Francisco)",0.0635,2.065,0,417.2771891,25.7108544,0,0,0,0,0.972,0,0,0,0,0.01148,0.00196,0,0.00728,0.00728,0,0,0,0,0,0,0.17,40,11.78,417.63,137817.9,0.85,0,1016,0.85,475,0.85,250,0.85,1000,0.52,0.34,0.07,0.07,0.6,0.2,0.6,0,0,0,29 +10,0,0,"High-Rise Mixed Use","High-Rise Mixed (Atwater Place, Portland",0.0635,1.605,0,232.7290428,19.16933457,0,0,0,0,0.971,0,0,0,0,0.01189,0.00203,0,0.0010556,0.0140244,0,0,0,0,0,0,0.28,23,8.48,294.03,97029.9,0.85,0,1310,0.85,475,0.85,250,0.85,1000,0.52,0.34,0.07,0.07,0.95,0.5,0.95,0,0,0,210 +11,"GGLO",0,"High-Rise Mixed Use","High-Rise Mixed (601 4th Ave, Seattle)",0.0635,1.935,0,162.219618,2256.056384,0,0,0,0,0.222,0.739,0,0,0,0.01599,0.00273,0,0.0014196,0.0188604,0,0,0,0,0,0,0.5,39,20.13,0,0,0.85,0,1020,0.85,550,0.85,250,0.85,1000,0.52,0.34,0.07,0.07,0.95,0.5,0.95,0,0,0,211 +13,0,0,"Mid-Rise Mixed Use","Mid-Rise Mixed (937 Glisan, Portland)",0.0635,1.835,0,248.1524568,43.78577836,0,0,0,0,0.938,0,0,0,0,0.02542,0.00434,0,0.025792,0.006448,0,0,0,0,0,0,0.2,16,9.06,248.29,81935.7,0.85,0,1268,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.99,0.5,0.99,0,0,0,313 +14,0,0,"Mid-Rise Mixed Use","Mid-Rise Mixed (The Edge, Portland)",0.0635,1.835,0,135.646434,327.613902,0,0,0,0,0.57,0.31,0,0,0,0.0492,0.0084,0,0.05616,0.00624,0,0,0,0,0,0,0.4,11,7.07,223.25,73672.5,0.85,0,1100,0.85,550,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,314 +15,0,0,"Mid-Rise Mixed Use","Mid-Rise Mixed (The Gregory Lofts, Portland)",0.0635,1.835,0,158.4860904,124.3535749,0,0,0,0,0.841,0.098,0,0,0,0.02501,0.00427,0,0.030134,0.001586,0,0,0,0,0,0,0.4,12,7.38,223.24,73669.2,0.85,0,1450,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.95,0.5,0.95,0,0,0,315 +17,0,0,"Low-Rise Mixed Use","Mid-Rise Mixed (Museum Place, Portland OR)",0.0635,1.845,0,153.2358779,98.604,0,0,0,0,0.7,0,0,0,0,0.123,0.021,0,0.1482,0.0078,0,0,0,0,0,0,0.36,8,5,239.1,78903,0.79,0,786,0.83,550,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.9,0.35,0.9,0,0,0,417 +18,"PK",0,"Low-Rise Mixed Use","Mid-Rise Mixed (Gaia Bldg, Berkeley)",0.0635,1.605,0,292.842,57.76056,0,0,0,0,0.87,0,0,0,0,0.0533,0.0091,0,0.0338,0.0338,0,0,0,0,0,0,0.02,7,5.7,138.75,45787.5,0.85,0,627,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.99,0.35,0.99,0,0,0,418 +19,0,0,"Low-Rise Mixed Use","Mid-Rise Mixed (Fine Arts, Berkeley)",0.0635,2.245,0,167.7296313,22.75890783,0,0,0,0,0.903,0,0,0,0,0.03977,0.00679,0,0.042874,0.007566,0,0,0,0,0,0,0.02,8,3.01,107.53,35484.9,0.85,0,600,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.98,0.35,0.98,0,0,0,419 +20,0,0,"Low-Rise Mixed Use","Mid-Rise Mixed (East End Gateway, Sacramento)",0.0635,2.245,0,140.1594857,15.37163621,0,0,0,0,0.932,0,0,0,0,0.02788,0.00476,0,0.0024752,0.0328848,0,0,0,0,0,0,0.02,8,2.9,171.94,56740.2,0.85,0,714,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,1,0.35,1,0,0,0,420 +21,"GGLO",0,"Low-Rise Mixed Use","Mid-Rise Mixed (Site 17, Seattle)",0.0635,2.245,0,193.7131434,8.624329768,0,0,0,0,0.976,0,0,0,0,0.00984,0.00168,0,0.005616,0.006864,0,0,0,0,0,0,0.02,7,4.61,197.8097001,65277.2,0.85,0,860,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,1,0.35,1,0,0,0,421 +22,"GGLO",0,"Low-Rise Mixed Use","Mid-Rise Mixed (Alcyone, Seattle)",0.0635,2.245,0,193.3692295,3.549819032,0,0,0,0,0.989,0,0,0,0,0.00451,0.00077,0,0.002288,0.003432,0,0,0,0,0,0,0.02,7,4.14,196.39,64808.7,0.85,0,784,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.99,0.35,0.99,0,0,0,422 +23,"HM",0,"Low-Rise Mixed Use","Mid-Rise Mixed (1885 University/New Californian, Berkeley)",0.0635,2.245,0,149.8249055,21.5424,0,0,0,0,0.9,0,0,0,0,0.041,0.007,0,0.0208,0.0312,0,0,0,0,0,0,0.02,5,3.2,154,50820,0.85,0,712,0.85,550,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.9,0.35,0.9,0,0,0,423 +24,"PK",0,"Low-Rise Mixed Use","Mid-Rise Mixed (Touriel Bldg, Berkeley)",0.0635,2.245,0,199.1208912,16.62506375,0,0,0,0,0.938,0,0,0,0,0.02542,0.00434,0,0.012896,0.019344,0,0,0,0,0,0,0.02,5,3.44,45.55,15031.5,0.85,0,600,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,1,0.35,1,0,0,0,424 +26,0,0,"Low-Rise Mixed Use","Low-Rise Mixed (Stone Way Apts, Seattle)",0.0635,1.57,0,97.88648378,18.37347044,0,0,0,0,0.903,0,0,0,0,0.03977,0.00679,0,0.0035308,0.0469092,0,0,0,0,0,0,0.2,4,2.43,154.93,51126.9,0.85,0,830,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.8,0.35,0.8,0,0,0,426 +27,0,0,"Low-Rise Mixed Use","Low-Rise Mixed (200 Second Street, Oakland)",0.0635,1.845,0,161.0270357,31.27722632,0,0,0,0,0.893,0,0,0,0,0.04387,0.00749,0,0.0038948,0.0517452,0,0,0,0,0,0,0.05,6,3.75,178,58740,0.85,0,770,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.98,0.35,0.98,0,0,0,427 +28,0,0,"Low-Rise Mixed Use","Low-Rise Mixed (Cabrini First Hill Apts, Seattle)",0.0635,1.845,0,160.850603,39.94598728,0,0,0,0,0.854,0,0,0,0,0.05986,0.01022,0,0.0053144,0.0706056,0,0,0,0,0,0,0.05,6,3.51,109.68,36194.4,0.85,0,690,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.95,0.35,0.95,0,0,0,428 +29,0,0,"Low-Rise Mixed Use","Low-Rise Mixed (Kinsey Flats, Cincinnati, OH)",0.0635,1.585,0,30.10777957,22.99758912,0,0,0,0,0.76,0,0,0,0,0.0984,0.0168,0,0.008736,0.116064,0,0,0,0,0,0,0.15,4,1.1646,131.7657576,43482.7,0.859,0,1100,0.85,450,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.95,0.35,0.95,0,0,0,429 +30,0,0,"Low-Rise Mixed Use","Low-Rise Mixed (Shattuck Lofts, Berkeley)",0.0635,2.245,0,177.7248,31.17978947,0,0,0,0,0.9,0,0,0,0,0.041,0.007,0,0.0208,0.0312,0,0,0,0,0,0,0.05,4,4,240.669,79420.77,0.85,0,750,0.85,475,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,1,0.35,1,0,0,0,430 +31,0,0,"Parking Structure/Mixed Use","Parking Structure/Mixed Use (Fahrenheit Condos + Petco Parkade, San Diego CA)",0.0635,1.835,0,12.342,14.8104,0,0,0,0,0.5,0,0,0,0,0.205,0.035,0,0.0182,0.2418,0,0,0,0,0,0,0.4,4,0.8,396,130680,0.85,5000,1200,0.85,1000,0.85,525,0.85,1000,0.52,0.34,0.07,0.07,0.2,0.3,0,0,0,0,531 +32,0,0,"Parking Structure/Mixed Use","Parking Structure/Mixed Use (2)",0.0635,1.835,0,19.7472,29.6208,0,0,0,0,0.5,0,0,0,0,0.205,0.035,0,0.0182,0.2418,0,0,0,0,0,0,0.35,5,1.6,528,174240,0.85,7117,1500,0.85,1000,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.32,0.3,0,0,0,0,532 +33,0,0,"Parking Structure/Mixed Use","Parking Structure/Mixed Use (3)",0.0635,1.835,0,24.684,44.4312,0,0,0,0,0.5,0,0,0,0,0.205,0.035,0,0.0182,0.2418,0,0,0,0,0,0,0.25,6,2.4,660,217800,0.85,6956,1800,0.85,1000,0.95,350,0.85,1000,0.52,0.34,0.07,0.07,0.4,0.3,0,0,0,0,533 +37,0,0,"Main Street Commercial/MU High (3-5 Floors)","Main Street Commercial/MU (3400 Cesar Chavez St, SF, CA)",0.0635,1.835,0,84.8746998,50.4878792,0,0,0,0,0.81,0,0,0,0,0.0779,0.0133,0,0.01976,0.07904,0,0,0,0,0,0,0.1,4,2.83,130.85,43180.5,0.85,0,1000,0.97,450,0.95,300,0.85,1000,0.52,0.34,0.07,0.07,0.95,0.5,0.95,0,0,0,637 +38,0,0,"Main Street Commercial/MU High (3-5 Floors)","Main Street Commercial/MU (Belmont Dairy, Portland OR)",0.0635,1.835,0,102.6482197,54.46452,0,0,0,0,0.78,0,0,0,0,0.0902,0.0154,0,0.09724,0.01716,0,0,0,0,0,0,0.2,5,2.2,56,16400,0.78,6280,710,0.93,450,0.95,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.85,0,0,0,638 +39,0,0,"Main Street Commercial/MU High (3-5 Floors)","Main Street Commercial/MU (Venice Renaissance, Venice CA)",0.0635,1.605,0,55.56689264,38.96723859,0,0,0,0,0.77,0,0,0,0,0.0943,0.0161,0,0.0598,0.0598,0,0,0,0,0,0,0.1,4,1.9,295,97350,0.96,0,1101,0.87,425,0.95,300,0.85,1000,0.52,0.34,0.07,0.07,0.7,0.5,0.7,0,0,0,639 +40,0,0,"Main Street Commercial/MU High (3-5 Floors)","Main Street Commercial/MU (International Place, Harrisburg, PN)",0.0635,2.105,0,135.5358306,87.0232,0,0,0,0,0.69,0,0,0,0,0.1271,0.0217,0,0.0806,0.0806,0,0,0,0,0,0,0.4,3,2.9,174.6962706,57649.77,0.58,3333,373,1,450,0.95,300,0.85,1000,0.52,0.34,0.07,0.07,0.95,0.5,0.95,0,0,0,640 +41,0,0,"Main Street Commercial/MU High (3-5 Floors)","Main Street Commercial/MU (Heilig-Levine, Raleigh NC)",0.0635,0,0,0,225.9356369,0,0,0,0,0,0.52,0,0,0,0.1968,0.0336,0,0.1248,0.1248,0,0,0,0,0,0,0.2,3,2.4,75.579736,24941.31,0.85,4012,1200,0.95,445,0.95,300,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,641 +44,0,0,"Main Street Commercial/MU Low (1-2 Floors)","Main Street Commercial/MU Low (480 Castro Street, San Francisco) (MU Walgreens)",0.0635,1.605,0,39.80597171,64.31945143,0,0,0,0,0.52,0,0,0,0,0.1968,0.0336,0,0.19968,0.04992,0,0,0,0,0,0,0.5,2,1.9,0,0,0.85,9152,919,0.85,525,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,1,0.6,1,0,0,0,744 +47,0,0,"Main Street Commercial/MU Low (1-2 Floors)","Main Street Commercial/MU Low (5488 College Avenue, Oakland) (MU Retail)",0.0635,2.105,0,43.20565498,53.15134206,0,0,0,0,0.52,0,0,0,0,0.1968,0.0336,0,0.1248,0.1248,0,0,0,0,0,0,0.5,2,1.6,35.82081149,0,0.85,8559,713,0.85,535,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.99,0.6,0.99,0,0,0,747 +52,0,0,"Skyscraper Residential","Skyscraper Residential (Rincon One, San Francisco)",0.0635,1.505,0,545.0088785,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5,60,18.9,545.0088785,179852.93,0.85,0,1284,0.85,500,0.85,300,0.85,1000,0,0.8,0.2,0,0.4,0.15,0.4,0,0,0,852 +53,0,0,"Skyscraper Residential","Skyscraper Residential (Langstaff W-12)",0.0635,1.635,0,672.4232308,26.16504,0,0,0,0,0.98,0,0,0,0,0.02,0,0,0,0,0,0,0,0,0,0,0.5,48,15.9,672.4232308,221899.67,0.85,0,858,0.85,450,0.85,300,0.85,1000,0,0.8,0.2,0,0.3313,0.15,0,0,0,0,853 +56,0,0,"High-Rise Residential","High-Rise Residential (AC Mid-Rise Res, 27+5 floors)",0.0635,1.685,0,165.9989189,28.06181053,0,0,0,0,0.94,0,0,0,0,0.06,0,0,0,0,0,0,0,0,0,0,0.25,32,6,199.1987027,65735.57,0.8,0,1184,0.85,475,0.85,300,0.85,1000,0,0.8,0.2,0,0.1875,0.15,0,0,0,0,956 +58,0,0,"High-Rise Residential","High-Rise Residential (Viridian, Nashville TN)",0.0635,1.735,0,552.4432941,22.99968,0,0,0,0,0.98,0,0,0,0,0.02,0,0,0,0,0,0,0,0,0,0,0.1,31,13.2,803.6,265188,0.9,0,918,1,500,0.85,300,0.85,1000,0,0.8,0.2,0,0.95,0.5,0.95,0,0,0,958 +59,0,0,"High-Rise Residential","High-Rise Residential (199 New Montgomery, SF)",0.0635,1.685,0,571.634715,35.4692624,0,0,0,0,0.974,0,0,0,0,0.026,0,0,0,0,0,0,0,0,0,0,0.2,20,16.58,275.86,91033.8,0.85,0,1046,0.85,450,0.85,300,0.85,1000,0,0.8,0.2,0,1,0.5,1,0,0,0,959 +60,0,0,"High-Rise Residential","High-Rise Residential (The Metropolitan, SF)",0.0635,1.505,0,403.3131418,1.990829558,0,0,0,0,0.998,0,0,0,0,0.002,0,0,0,0,0,0,0,0,0,0,0.25,27,12.77,405.75,133897.5,0.85,0,1170,0.85,475,0.85,300,0.85,1000,0,0.8,0.2,0,0.5,0.2,0.5,0,0,0,960 +61,0,0,"High-Rise Residential","High-Rise Residential (Pine & Franklin, SF)",0.0635,1.635,0,348.5296198,14.62527,0,0,0,0,0.985,0,0,0,0,0.015,0,0,0,0,0,0,0,0,0,0,0.2,24,11.85,388.89,128333.7,0.85,0,1240,0.85,450,0.85,300,0.85,1000,0,0.8,0.2,0,0.97,0.5,0.97,0,0,0,961 +62,0,1,"Urban Mid-Rise Residential","Mid-Rise Residential (Langstaff E-09)",0.0635,1.685,0,230.4563341,22.19209143,0,0,0,0,0.96,0.02,0,0,0,0.02,0,0,0,0,0,0,0,0,0,0,0.3,10,5.9,110,36300,0.85,0,910,0.85,450,0.85,350,0.85,1000,0,0.8,0.2,0,0.59,0.2,0,0,0,0,1062 +63,0,0,"Urban Mid-Rise Residential","Mid-Rise Mixed (Eddy + Taylor Family Housing, SF)",0.0635,2.245,0,298.3361901,46.43949024,0,0,0,0,0.922,0,0,0,0,0.078,0,0,0,0,0,0,0,0,0,0,0.03,14,8.04,150,49500,0.85,0,920,0.85,500,0.85,300,0.85,1000,0,0.8,0.2,0,0.9,0.5,0.9,0,0,0,1063 +64,0,0,"Urban Mid-Rise Residential","Mid-Rise Mixed (Cubix Yerba Buena, SF)",0.0635,2.245,0,643.7853108,44.9199432,0,0,0,0,0.91,0,0,0,0,0.09,0,0,0,0,0,0,0,0,0,0,0.01,8,6.74,31.7,31.7,0.8,0,332,0.85,500,0.85,300,0.85,1000,0,0.8,0.2,0,0.9,0.5,0.9,0,0,0,1064 +65,0,0,"Urban Mid-Rise Residential","Mid-Rise Residential (AC Res Type 2/MU)",0.0635,2.245,0,123.4616959,17.34303346,0,0,0,0,0.94,0,0,0,0,0.06,0,0,0,0,0,0,0,0,0,0,0.01,17,4.2,65,21450,0.85,0,1184,0.85,538,0.85,350,0.85,1000,0,0.8,0.2,0,0.2471,0.15,0,0,0,0,1065 +66,0,0,"Urban Mid-Rise Residential","Mid-Rise Residential (CapMetro Apt/Condo Hi)",0.0635,1.635,0,99.9702,24.684,0,0,0,0,0.9,0,0,0,0,0.1,0,0,0,0,0,0,0,0,0,0,0.65,8,3,50,16500,0.85,0,1000,0.85,450,0.85,350,0.85,1000,0,0.8,0.2,0,0.97,0.5,0.97,0,0,0,1066 +67,0,1,"Urban Podium Multi-Family","Low-Rise Residential (AC Low Rise Res/MU)",0.0635,1.935,0,91.00140203,6.19394052,0,0,0,0,0.97,0,0,0,0,0.03,0,0,0,0,0,0,0,0,0,0,0.1,5,3,109.2016824,36036.56,0.85,0,1184,0.85,538,0.85,350,0.85,1000,0,0.8,0.2,0,0.6,0.2,0,0,0,0,1167 +68,0,0,"Urban Podium Multi-Family","Low-Rise Residential (Alameda MF with Ret)",0.0635,2.245,0,47.60485714,14.8104,0,0,0,0,0.9,0,0,0,0,0.1,0,0,0,0,0,0,0,0,0,0,0.05,5,2,57.12582857,18851.52,0.85,0,1400,0.85,500,0.85,350,0.85,1000,0,0.8,0.2,0,0.4,0.15,0,0,0,0,1168 +69,0,0,"Urban Podium Multi-Family","Low-Rise Residential (SACOG 45. Intense Urban Res)",0.0635,2.245,0,125.5254,20.216196,0,0,0,0,0.95,0,0,0,0,0.05,0,0,0,0,0,0,0,0,0,0,0.05,6,2.73,150.63048,49708.06,0.85,0,765,0.85,250,0.85,350,0.85,1000,0,0.8,0.2,0,0.455,0.15,0,0,0,0,1169 +70,0,0,"Urban Podium Multi-Family","Low-Rise Residential (SCAG Apt/Condo Hi)",0.0635,2.245,0,111.078,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,4,3,133.2936,43986.89,0.85,0,1000,0.85,750,0.85,350,0.85,1000,0,0.8,0.2,0,0.75,0.5,0,0,0,0,1170 +72,0,0,"Urban Podium Multi-Family","Low-Rise Residential (25-35 Dolores, SF)",0.0635,2.245,0,103.9116774,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,4,2.61,88.9,29337,0.85,0,930,0.85,450,0.85,350,0.85,1000,0,0.8,0.2,0,0.6525,0.5,0,0,0,0,1172 +73,0,0,"Urban Podium Multi-Family","Low-Rise Residential (Ironhorse Family Apartments, Oakland)",0.0635,1.835,0,63.42077228,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,4,1.73,76.10492673,25114.63,0.85,0,1010,0.85,500,0.85,350,0.85,1000,0,0.8,0.2,0,0.4325,0.2,0,0,0,0,1173 +74,0,0,"Urban Podium Multi-Family","Low-Rise Residential (MODA Lofts, Stapleton, Denver)",0.0635,2.245,0,79.96040426,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.25,4,2.03,127.42,42048.6,0.85,0,940,0.85,500,0.85,350,0.85,1000,0,0.8,0.2,0,0.97,0.5,0.97,0,0,0,1174 +75,0,0,"Urban Podium Multi-Family","Low-Rise Residential (Darling Florist Bldg, Berkeley)",0.0635,1.635,0,199.9404,33.3234,0,0,0,0,0.9,0,0,0,0,0.1,0,0,0,0,0,0,0,0,0,0,0.2,5,4.5,239.92848,79176.4,0.85,0,750,0.85,500,0.85,350,0.85,1000,0,0.8,0.2,0,0.98,0.5,0.98,0,0,0,1175 +80,0,0,"Standard Podium Multi-Family","Low-Rise Residential (Avalon Apts (Cahill Park), San Jose)",0.0635,1.835,0,59.2416,11.9,0,0,0,0,0.919679084,0,0,0,0,0.080320916,0,0,0,0,0,0,0,0,0,0,0.55,4,2,74.052,24437.16,0.85,0,1150,0.85,500,0.85,350,0.85,1000,0,0.8,0.2,0,0.6,0.2,0.6,0,0,0,1280 +82,0,0,"Standard Podium Multi-Family","Low-Rise Residential (Ironhorse Family Apartments, Oakland) 1",0.0635,2.245,0,63.42077228,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,4,1.73,79.27596535,26161.07,0.85,0,1010,0.85,475,0.85,350,0.85,1000,0,0.8,0.2,0,0.4325,0.15,0,0,0,0,1282 +83,0,0,"Standard Podium Multi-Family","Low-Rise Residential (MODA Lofts, Stapleton, Denver) 2",0.0635,2.245,0,79.96040426,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.25,4,2.03,127.42,42048.6,0.85,0,940,0.85,500,0.85,350,0.85,1000,0,0.8,0.2,0,0.97,0.5,0.97,0,0,0,1283 +87,0,0,"Suburban Multifamily Apt/Condo","Multifamily Apt/Condo (Lenzen Square, San Jose)",0.0635,1.845,0,54.59766102,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,3,0.87,109.195322,36034.46,0.85,0,590,0.85,500,0.85,350,0.85,1000,0,0,0,0,0.6,0.5,0.6,0,0,0,1387 +88,0,0,"Suburban Multifamily Apt/Condo","Multifamily Apt/Condo (Linden Court, Oakland)",0.0635,2.245,0,49.02033803,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,3,0.94,98.04067606,32353.42,0.85,0,710,0.85,500,0.85,350,0.85,1000,0,0,0,0,0.8,0.5,0.8,0,0,0,1388 +89,0,0,"Suburban Multifamily Apt/Condo","Multi-Family Apt/Condo (Sonoma Villero, Bothell, WA)",0.0635,2.245,0,24.0623883,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,3,0.8,48.1247766,4764.35,0.85,0,1231,0.85,475,0.85,350,0.85,1001,0,0,0,0,0.65,0.5,0.65,0,0,0,1389 +90,0,0,"Suburban Multifamily Apt/Condo","Multi-Family Apt/Condo (Town Lofts, Stapleton, Denver)",0.0635,2.245,0,37.88044615,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,3,1.33,77.44,7666.56,0.85,0,1300,0.85,475,0.85,350,0.85,1000,0,0,0,0,0.854,0.5,0,0,0,0,1390 +91,0,0,"Suburban Multifamily Apt/Condo","Multifamily Apt/Condo (Mabuhay Court, San Jose)",0.0635,1.935,0,56.13619355,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,3,0.94,112.2723871,37049.89,0.85,0,620,0.85,475,0.85,350,0.85,1000,0,0,0,0,0.75,0.5,0.75,0,0,0,1391 +93,0,0.75,"Urban Townhome/Live-Work","Townhome/Live-Work (Alameda Small Townhouse)",0.0411,2.235,0,30,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.82,45,14850,1,1452,1191,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.41,0.15,0,0,0,0,1493 +95,0,0,"Urban Townhome/Live-Work","Townhome/Live-Work (Denver Brownstone, Stapleton, Denver)",0.0411,2.185,0,19.16129032,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,4,1.5,38.32258065,12646.45,1,2897,3410,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.375,0.15,0,0,0,0,1495 +96,0,0,"Urban Townhome/Live-Work","Townhome/Live-Work (Pearl Townhome, Portland)",0.0411,2.245,0,25.80128068,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3,2,1.11,32.25160085,10643.03,1,1742,1874,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.555,0.15,0,0,0,0,1496 +97,0,0,"Urban Townhome/Live-Work","Townhome/Live-Work (Penthouse Row Homes, Stapleton, Denver)",0.0411,2.235,0,27.49290804,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.25,3,1.24,68.73227011,22681.65,1,2219,1965,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.85,0.5,0.85,0,0,0,1497 +98,0,0,"Urban Townhome/Live-Work","Townhome/Live-Work (Backyard Row Home, Stapleton, Denver)",0.0411,2.245,0,24.02589928,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,3,0.92,48.05179856,15857.09,1,1917,1668,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.3067,0.2,0,0,0,0,1498 +99,0,0,"Urban Townhome/Live-Work","Townhome/Live-Work (Sky Terrace, Stapleton, Denver)",0.0411,2.635,0,14.99668019,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,3,0.7,37.49170048,12372.26,1,2519,2033,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.2333,0.2,0,0,0,0,1499 +101,0,0.75,"Standard Townhome","Townhome/Live-Work (Alameda Small Townhouse) 2",0.0411,1.935,0,30,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.82,60,19800,1,1452,1191,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.41,0.5,0,0,0,0,15101 +102,0,0,"Standard Townhome","Townhome/Live-Work (SEGA Townhouse)",0.0411,2.235,0,21.78,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.25,2,0.75,43.56,14374.8,1,2000,1500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.375,0.5,0,0,0,0,15102 +106,0,0,"Standard Townhome","Townhome/Live-Work (Backyard Row Home, Stapleton, Denver) 2",0.0411,2.335,0,24.02589928,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,3,0.92,44,14520,1,1917,1668,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.3067,0.5,0,0,0,0,15106 +107,0,0,"Standard Townhome","Townhome/Live-Work (Sky Terrace, Stapleton, Denver) 2",0.0411,2.375,0,14.99668019,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.45,3,0.7,32.27,10649.1,1,2519,2033,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.2333,0.5,0,0,0,0,15107 +109,0,0,"Garden Apartment","Garden Apartment (Corte Bella, Irvine CA)",0.0411,2.235,0,21.55681511,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.35,3,0.75,56.56,18664.8,1,0,1516,0.85,600,0.92,300,0.85,1000,0,0,0,0,0.6,0.5,0.6,0,0,0,16109 +110,0,0,"Garden Apartment","Garden Apartment (Victoria Townhomes, Seattle WA)",0.0411,2.375,0,24.12782275,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3,3,1.3,78.7,25971,1,0,2347,0.85,600,0.92,300,0.85,1000,0,0,0,0,0.4333,0.5,0,0,0,0,16110 +111,0,0,"Garden Apartment","Stapleton Garden Apts (F1 Affordable Townhomes)",0.0411,2.285,0,30.89376431,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.35,3,0.806,45,14850,1,1409,1136,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.2687,0.5,0,0,0,0,16111 +120,0,0,"Very Small Lot 3000","Very Small Lot 2500 (Garden Courts, Stapleton, Denver)",0.031,2.44,0,15.92356688,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0.709176303,31.84713376,0,1,2736,1940,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.5959,0.5,0,0,0,0,17120 +123,0,0,"Small Lot 4000","Small Lot 4000 (John Laing SF, Stapleton, Denver)",0.031,2.85,0,10.41666667,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3,2,0.679379017,20.83,0,1,4182,2841,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.4975,0.5,0,0,0,0,18123 +124,0,0,"Small Lot 4000","Small Lot 4000 (Town Square, Sapleton, Denver)",0.031,2.45,0,10.29866117,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3,2,0.612340047,20.6,0,1,4230,2590,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.4622,0.5,0,0,0,0,18124 +125,0,0,"Small Lot 4000","Small Lot 4000 (Average, Albany)",0.031,2.54,0,10.89,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3,2,0.375,20.6,0,1,4000,1500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.3436,0.5,0,0,0,0,18125 +126,0,0,"Small Lot 4000","Small Lot 4000 (Alameda SF Detached, 1-2 floors)",0.031,2.255,0,10.89,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.825,20.6,0,1,4000,3300,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.5686,0.5,0,0,0,0,18126 +129,0,0,"Medium Lot 5500","Daybreak 5500",0.031,2.72,0,7.92,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.347,15.84,0,1,5500,1910,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.2935,0.5,0,0,0,0,19129 +131,0,"http://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=st.+francis+wood+san+francisco&sll=37.768463,-122.261667&sspn=0.02412,0.038581&ie=UTF8&hq=&hnear=St+Francis+Wood,+San+Francisco,+California&t=h&z=15","Medium Lot 5500","Medium Lot (Average, St. Francis Wood, San Francisco)",0.031,2.64,0,8.712,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.5,17.424,0,1,5000,2500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.597,0.5,0.321,0.276,0,0.403,19131 +133,0,0,"Medium Lot 5500","Medium Lot 5500 (Kentlands, Stapleton, Denver)",0.031,2.72,0,7.92,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.6,2,0.436363636,15.84,0,1,5500,2400,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.3382,0.5,0,0,0,0,19133 +135,0,0,"Large Lot 7500","Large Lot 7500 sf (SACOG B. SF Large Lot, 1-2 floors)",0.031,3.41,0,5.808,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.373333333,17.424,0,1,7500,2800,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.3187,0.5,0,0,0,0,20135 +136,0,0,"Large Lot 7500","Large Lot 7500 (Average, View Park, Los Angeles)",0.031,3.06,0,5.808,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.5,17.424,0,1,7500,4000,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.382,0.5,0,0,0,0,20136 +137,0,"http://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=franklin+park+alameda&sll=37.766389,-122.254014&sspn=0.01206,0.01929&ie=UTF8&hq=Franklin+Park&hnear=Franklin+Park,+Alameda,+California+94501&t=h&z=15&iwloc=A","Large Lot 7500","Large Lot (Average, Gold Coast, Alameda, CA",0.031,3.41,0,5.808,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.5,17.424,0,1,7500,4000,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.382,0.5,0,0,0,0,20137 +138,0,0,"Large Lot 7500","Large Lot 7500 (Estate Home, Stapleton, Denver)",0.031,2.81,0,5.58347292,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.4,3,0.612695146,16.75041876,0,1,7802,4780,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.88,0.5,0.88,0,0,0,20138 +139,0,0.25,"Estate Lot","Estate Lot (SACOG 2. Very Low Den Res, , 1-2 floors)",0.031,2.81,0,4.356,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.28,17.424,0,1,10000,2800,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.272,0.5,0,0,0,0,21139 +140,0,0,"Estate Lot","Estate Lot (SCAG Large Lot, 1-2 floors)",0.031,3.41,0,2.178,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.15,8.712,0,1,20000,3000,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.141,0.5,0,0,0,0,21140 +141,0,"http://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=632+n+foothill+beverly+hills+ca&sll=34.08126,-118.402534&sspn=0.012707,0.01929&ie=UTF8&hq=&hnear=632+N+Foothill+Rd,+Beverly+Hills,+Los+Angeles,+California+90210&ll=34.081491,-118.402201&spn=0.006318,0.009645&t=h&z=17","Estate Lot","Estate Lot (Average, Beverly Hills)",0.031,2.81,0,2.904,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.3,11.616,0,1,15000,5000,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.238,0.5,0,0,0,0,21141 +142,0,"http://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=old+palo+alto+&sll=37.734653,-122.468027&sspn=0.024131,0.038581&ie=UTF8&hq=&hnear=Old+Palo+Alto,+Palo+Alto,+California&t=h&z=15","Estate Lot","Estate Lot (Average, Old Palo Alto)",0.031,3.41,0,2.178,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.2,8.712,0,1,20000,5000,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.166,0.5,0,0,0,0,21142 +143,0,0,"Estate Lot","Estate Lot (Daybreak Estate, South Jordan)",0.031,2.81,0,4.444444444,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.327925722,17.77777778,0,1,9801,3214,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.2986,0.5,0,0,0,0,21143 +144,0,0,"Estate Lot","Estate Lot (Windemere Estate, San Ramon)",0.031,3.41,0,4.18765622,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.476446837,16.75062488,0,1,10402,4956,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.3651,0.5,0,0,0,0,21144 +145,0,0.027777777,"Rural Residential","Rural Residential (SACOG 1. Rural Res, 1-2 floors)",0.031,3.41,0,0.5445,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.068870523,1.6335,0,1,80000,2500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0468,0.5,0,0,0,0,22145 +146,0,0,"Rural Residential","Rural Residential (SCAG Rural, 1-2 floors)",0.031,2.81,0,0.27225,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.4,2,0.068870523,0.81675,0,1,160000,3000,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0406,0.5,0,0,0,0,22146 +147,0,0,"Rural Residential","Rural Residential (Prairie Crossing Rural SF, Grayslake)",0.031,3.41,0,1.512447484,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.138814624,4.537342453,0,1,28801,3998,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.1038,0.5,0,0,0,0,22147 +148,0,0,"Rural Residential","Rural Residential (SEGA Rural, 1-2 floors)",0.031,2.81,0,0.5445,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.4,2,0.068870523,1.6335,0,1,80000,3000,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0468,0.5,0,0,0,0,22148 +149,0,0.027777777,"Rural Ranchette","Rural/Ranchette (AFT 1.5 acre lot)",0.031,3.41,0,0.666666667,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,2,0.038261402,3.333333333,0,1,65340,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0444,0.5,0,0,0,0,23149 +150,0,0,"Rural Ranchette","Ranchette 1 (near Fresno)",0.031,2.81,0,0.5445,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,2,0.3,2.7225,0,1,80000,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.1706,0.5,0,0,0,0,23150 +151,0,0,"Rural Ranchette","Ranchette 2",0.031,3.41,0,0.4356,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,2,0.002,2.178,0,1,100000,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0175,0.5,0,0,0,0,23151 +152,0,0,"Rural Ranchette","Rural/Ranchette (AFT 5 acre lot)",0.031,2.81,0,0.2,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,2,0.011478421,1,0,1,217800,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0133,0.5,0,0,0,0,23152 +153,0,0,"Rural Ranchette","Ranchette 6 (Near Fresno)",0.031,2.81,0,0.207428571,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.02,1.037142857,0,1,210000,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0179,0.5,0,0,0,0,23153 +154,0,0,"Rural Ranchette","Rural/Ranchette (AFT 10 acre lot)",0.031,3.41,0,0.1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.00573921,0.5,0,1,435600,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0067,0.5,0,0,0,0,23154 +155,0,0,"Rural Ranchette","Ranchette 4 (near Chowchilla)",0.031,2.81,0,0.121,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.01,0.605,0,1,360000,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0096,0.5,0,0,0,0,23155 +156,0,0,"Rural Ranchette","Rural/Ranchette (AFT 20 acre lot)",0.031,3.41,0,0.05,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,2,0.002869605,0.25,0,1,871200,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0033,0.5,0,0,0,0,23156 +157,0,0,"Rural Ranchette","Ranchette 5 (near Fresno)",0.031,2.81,0,0.047868132,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.005,0.239340659,0,1,910000,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0043,0.5,0,0,0,0,23157 +158,0,0,"Rural Ranchette","Ranchette 3 (near Boonville)",0.031,2.81,0,0.04356,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,2,0.003,0.2178,0,1,1000000,3500,0.85,750,0.85,350,0.85,1000,0,0,0,0,0.0032,0.5,0,0,0,0,23158 +159,0,0,"Skyscraper Office","Transbay Tower",0.0635,0,0,0,2765.71152,0,0,0,0,0,0.9,0,0,0,0.041,0.007,0,0.00364,0.04836,0,0,0,0,0,0,0.2,80,19.5,181,59730,0.85,0,0,0.98,500,0.85,250,0.85,0,0.52,0.34,0.07,0.07,0.15,0.8,0.15,0,0,0,24159 +160,0,0,"Skyscraper Office","Skyscraper Office (US Bank Tower, Los Angeles)",0.0635,0,0,0,3107.295972,0,0,0,0,0,0.97,0,0,0,0.0123,0.0021,0,0.001092,0.014508,0,0,0,0,0,0,0.2,73,21.3,1498.44222,494485.93,0.85,0,1000,0.85,500,0.85,250,0.85,0,0.52,0.34,0.07,0.07,0.8,0.5,0.8,0,0,0,24160 +161,0,0,"Skyscraper Office","Skyscraper Office (Washington Mutual Tower, Seattle)",0.0635,0,0,0,2917.6488,0,0,0,0,0,0.97,0,0,0,0.0123,0.0021,0,0.001092,0.014508,0,0,0,0,0,0,0.2,55,20,1406.988,464306.04,0.85,0,1000,0.85,500,0.85,250,0.85,0,0.52,0.34,0.07,0.07,0.5,0.3,0.5,0,0,0,24161 +163,0,0,"Skyscraper Office","Aon Center (Los Angeles, CA)",0.0635,0,0,0,4867.882272,0,0,0,0,0,0.98,0,0,0,0.0082,0.0014,0,0.000728,0.009672,0,0,0,0,0,0,0.2,62,33.2,952,314160,0.85,0,0,0.85,500,0.85,250,0,0,0.52,0.34,0.07,0.07,0.95,0.5,0.95,0,0,0,24163 +164,0,0,"Skyscraper Office","Bank of America Center (Los Angeles, CA)",0.0635,0,0,0,1085.009904,0,0,0,0,0,0.98,0,0,0,0.0082,0.0014,0,0.000728,0.009672,0,0,0,0,0,0,0.2,55,7.4,534,176220,0.85,0,0,0.85,500,0.85,250,0.85,0,0.52,0.34,0.07,0.07,0.2,0.2,0.2,0,0,0,24164 +169,0,"http://casestudies.uli.org/Profile.aspx?j=8218&p=5&c=4, http://www.som.com/content.cfm/john_hancock_center, http://www.johnhancockcenterchicago.com/Index3.htm","High-Rise Office","High-Rise Mixed (Tabor Center, Denver)",0.0635,0,0,0,1702.93464,0,0,0,0,0,0.9,0,0,0,0.041,0.007,0,0.00364,0.04836,0,0,0,0,0,0,0.3,31,11,477,157410,0.85,0,637,0.85,500,0.94,250,0.85,1000,0.52,0.34,0.07,0.07,0.6,0.5,0.6,0,0,0,25169 +170,"Pelli Clarke Pelli",0,"High-Rise Office","High-Rise Office (560 Mission Street, San Francisco)",0.0635,0,0,0,2802.720096,0,0,0,0,0,0.992,0,0,0,0.00328,0.00056,0,0.001664,0.002496,0,0,0,0,0,0,0.2,31,19,351.747,116076.51,0.85,0,1000,0.85,500,0.85,250,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.85,0,0,0,25170 +171,"Heller Manus",0,"High-Rise Office","High Rise Office (555 Mission Street, San Francisco)",0.0635,0,0,0,2430.478464,0,0,0,0,0,0.994,0,0,0,0.00246,0.00042,0,0.001248,0.001872,0,0,0,0,0,0,0.2,33,16.46,190.6,62898,0.85,0,1000,0.85,500,0.85,250,0.85,1000,0.52,0.34,0.07,0.07,0.8,0.5,0.8,0,0,0,25171 +172,"Heller Manus",0,"High-Rise Office","High-Rise Office (55 Second Street, San Francisco)",0.0635,0,0,0,2143.06488,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,26,14.47,191.56,63214.8,0.85,0,1000,0.85,500,0.85,250,0.85,1000,0.52,0.34,0.07,0.07,0.9,0.5,0.9,0,0,0,25172 +173,0,0,"High-Rise Office","High-Rise Office (SACOG 46. CBD Ofice)",0.0635,0,0,0,486.632718,0,0,0,0,0,0.95,0,0,0,0.0205,0.0035,0,0.00182,0.02418,0,0,0,0,0,0,0.2,20,3.37,62.38881,20588.31,0.85,0,1000,0.85,500,0.85,250,0.85,1000,0.52,0.34,0.07,0.07,0.1685,0.2,0,0,0,0,25173 +174,0,1,"Mid-Rise Office","Mid-Rise Office (Langstaff W-05)",0.0635,0,0,0,1135.464,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,9,4.6,85.1598,28102.73,0.85,0,1000,0.85,500,0.85,150,0.85,1000,0.52,0.34,0.07,0.07,0.5111,0.2,0,0,0,0,26174 +176,0,0,"Mid-Rise Office","Mid-Rise Office (Langstaff W-04)",0.0635,0,0,0,1055.98152,0,0,0,0,0,0.9,0,0,0,0.041,0.007,0,0.00364,0.04836,0,0,0,0,0,0,0.2,9,4.6,85.1598,28102.73,0.85,0,1000,0.85,500,0.85,150,0.85,1000,0.52,0.34,0.07,0.07,0.5111,0.2,0,0,0,0,26176 +178,0,0,"Mid-Rise Office","Mid-Rise Office (SCAG City Center Office, 6-15 floors)",0.0635,0,0,0,312.6052286,0,0,0,0,0,0.95,0,0,0,0.0205,0.0035,0,0.00182,0.02418,0,0,0,0,0,0,0.2,11,3,55.539,18327.87,0.85,0,1000,0.85,500,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.2727,0.3,0,0,0,0,26178 +179,"ZGF",0,"Mid-Rise Office","Mid-Rise Office (EPA Headquarters (Region 8), Denver)",0.0635,0,0,0,590.3002286,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,9,5.58,31.68,10454.4,0.85,0,1000,0.85,500,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.62,0.5,0,0,0,0,26179 +180,0,0,"Mid-Rise Office","Mid-Rise Office (SACOG 8. Hi Intensity Office)",0.0635,0,0,0,203.198688,0,0,0,0,0,0.95,0,0,0,0.0205,0.0035,0,0.00182,0.02418,0,0,0,0,0,0,0.2,10,1.68,31.10184,10263.61,0.85,0,1000,0.85,500,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.168,0.2,0,0,0,0,26180 +181,0,1,"Low-Rise Office","Low-Rise Office (AC Low Rise Office)",0.0635,0,0,0,275.2862454,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,4,2,74.052,24437.16,0.85,0,1000,0.85,538,0.85,269,0.85,1000,0.52,0.34,0.07,0.07,0.5,0.4,0,0,0,0,27181 +182,"Pickard Chilton",0,"Low-Rise Office","Low-Rise Office (CalPERS Headquarters, Sacramento)",0.0635,0,0,0,474.9906857,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,6,4.49,177.94,58720.2,0.85,0,1000,0.85,500,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.3,0.3,0.3,0,0,0,27182 +183,"Weber Thompson",0,"Low-Rise Office","Low-Rise Office (The Terry Thomas, Seattle)",0.0635,0,0,0,462.9612872,0,0,0,0,0,0.925,0,0,0,0.03075,0.00525,0,0.00273,0.03627,0,0,0,0,0,0,0.2,6,3.06,250,82500,0.85,0,1000,0.85,500,0.85,235,0.85,1000,0.52,0.34,0.07,0.07,0.8,0.4,0.8,0,0,0,27183 +184,"NBBJ",0,"Low-Rise Office","Low-Rise Office (223 Yale @ Alley24, Seattle)",0.0635,0,0,0,802.3869428,0,0,0,0,0,0.889,0,0,0,0.04551,0.00777,0,0.0040404,0.0536796,0,0,0,0,0,0,0.2,5,4.82,252.53,83334.9,0.85,0,1000,0.85,500,0.85,208,0.85,1000,0.52,0.34,0.07,0.07,0.98,0.5,0.98,0,0,0,27184 +185,"HOK",0,"Low-Rise Office","Low-Rise Office (Symantec Headquarters, Culver City)",0.0635,0,0,0,124.40736,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3,5,1.26,155,51150,0.85,0,1000,0.85,500,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.252,0.3,0,0,0,0,27185 +186,0,0,"Low-Rise Office","Low-Rise Office (SACOG 98. Mod Inten. Office)",0.0635,0,0,0,123.370632,0,0,0,0,0,0.95,0,0,0,0.0205,0.0035,0,0.00182,0.02418,0,0,0,0,0,0,0.05,4,1.02,37.76652,12462.95,0.85,0,1000,0.85,500,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.255,0.4,0,0,0,0,27186 +187,"GGLO",0,"Low-Rise Office","Low-Rise Office (R.D. Merrill Building, Seattle)",0.0635,0,0,0,291.9764571,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,3,2.76,78.57,25928.1,0.85,0,1000,0.85,500,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.92,0.5,0,0,0,0,27187 +188,0,0,"Low-Rise Office","Low-Rise Office (SEGA Low Rise Office, 4-6 floors)",0.0635,0,0,0,105.7885714,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,4,1,37.026,12218.58,0.85,0,1000,0.85,500,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.25,0.3,0,0,0,0,27188 +189,0,0,"Main Street Commercial (Retail + Office/Medical)","Main Street Commercial/MU Low (4185 Piedmont Avenue, Oakland) (Dentist Office)",0.0635,0,0,0,133.9988571,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.05,2,1.9,78,0,0.85,5000,750,0.85,500,0.85,525,0.85,1000,0.52,0.34,0.07,0.07,0.4,0.15,0.4,0,0,0,28189 +190,0,0,"Main Street Commercial (Retail + Office/Medical)","Main Street Commercial/MU Low (1853 Solano Avenue, Berkeley) (Zachary's Pizza)",0.0635,0,0,0,56.42057143,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0,0.52,0,0,0,0,0,0,0.05,1,0.8,29.6208,0,0.85,7117,750,0.85,525,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,28190 +191,0,0,"Main Street Commercial (Retail + Office/Medical)","Main Street Commercial/MU Low (3170 College Avenue, Berkeley) (MU Noah's Bagels)",0.0635,0,0,0,70.52571429,0,0,0,0,0,0.5,0,0,0,0.205,0.035,0,0.026,0.234,0,0,0,0,0,0,0.05,2,0.8,59,0,0.85,13895,750,0.85,525,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,28191 +192,0,0,"Main Street Commercial (Retail + Office/Medical)","Main Street Commercial (Mechanics Bank, Kensington CA)",0.0635,0,0,0,98.736,0,0,0,0,0,0.8,0,0,0,0.082,0.014,0,0.00104,0.10296,0,0,0,0,0,0,0.2,1,1,57.2,0,0.85,0,1000,0.85,525,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.3,0.15,0.3,0,0,0,28192 +193,0,0,"Main Street Commercial (Retail + Office/Medical)","Main Street Commercial/MU Low (960 Cole Street, San Francisco) (Alpha Market)",0.0635,0,0,0,63.47314286,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.026,0.494,0,0,0,0,0,0,0.25,1,0.9,3.33234,0,0.85,3600,750,0.85,525,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,28193 +194,0,0,"Main Street Commercial (Retail + Office/Medical)","Main Street Commercial/MU Low (1601 N Main Street, Walnut Creek) (MU Instrument Sales)",0.0635,0,0,0,87.27557143,0,0,0,0,0,0.25,0,0,0,0.3075,0.0525,0,0.195,0.195,0,0,0,0,0,0,0.25,2,1.1,4.07286,0,0.85,8196,750,0.85,525,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.98,0.5,0.98,0,0,0,28194 +195,0,0,"Main Street Commercial (Retail + Office/Medical)","Main Street Commercial/MU Low (1616 N Main Street, Walnut Creek) (MU Crepe Vine)",0.0635,0,0,0,110.0201143,0,0,0,0,0,0.4,0,0,0,0.246,0.042,0,0.0156,0.2964,0,0,0,0,0,0,0.15,2,1.3,4.81338,0,0.85,6956,750,0.85,525,0.85,350,0.85,1000,0.52,0.34,0.07,0.07,0.5,0.15,0.5,0,0,0,28195 +196,0,0,"Parking Structure+Ground-Floor Retail","Parking Structure + Ground-Floor Retail (15th and Pearl Structure, Boulder, CO))",0.0635,0,0,0,69.59228571,0,0,0,0,0,0.5,0,0,0,0.205,0.035,0,0.0182,0.2418,0,0,0,0,0,0,0.3,6,5.5,726,239580,0.85,5000,750,0.1,500,0.2,525,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,29196 +197,0,0,"Parking Structure+Ground-Floor Retail","Parking Structure + Ground-Floor Retail (8th and Hope, Los Angeles CA)",0.0635,0,0,0,39.6396,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.3,7,6.5,632,208560,0.85,7117,750,0.07,500,0.2,350,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,29197 +198,0,0,"Parking Structure+Ground-Floor Retail","Parking Structure + Ground-Floor Retail (3)",0.0635,0,0,0,21.78,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.4,3,2.5,381.15,125779.5,0.85,6956,750,0.1,500,0.2,350,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,29198 +199,0,0,"Parking Structure","Parking Structure (1)",0.0635,0,0,0,3.0492,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.2,4,3.5,528,174240,0.85,5000,750,0.01,500,0.2,525,0.85,1000,0.52,0.34,0.07,0.07,0.875,0.5,0,0,0,0,30199 +200,0,0,"Parking Structure","Parking Structure (Oak & Central, Alameda)",0.0635,0,0,0,0,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.2,3,2.5,435.6,0,0,19000,0,0.01,500,0.2,400,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,30200 +201,0,0,"Parking Structure","Parking Structure (2)",0.0635,0,0,0,3.9204,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.2,5,4.5,660,217800,0.85,7117,750,0.01,500,0.2,350,0.85,1000,0.52,0.34,0.07,0.07,0.9,0.5,0,0,0,0,30201 +202,0,0,"Parking Structure","Parking Structure (Jack London Market, Oakland)",0.0635,0,0,0,0,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.208,0.312,0,0,0,0,0,0,0.2,5,4.5,784.08,0,0.85,0,0,0.01,500,0.2,400,0.85,1000,0.52,0.34,0.07,0.07,0.98,0.5,0.98,0,0,0,30202 +203,0,0,"Parking Structure","Parking Structure (3)",0.0635,0,0,0,4.7916,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.2,6,5.5,792,261360,0.85,6956,750,0.01,500,0.95,350,0.85,1000,0.52,0.34,0.07,0.07,0.9167,0.5,0,0,0,0,30203 +207,0,0,"Office Park High","Office Park High (SEGA Office Park 0.35, 2-4 floors)",0.0635,0,0,0,25.9182,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,3,0.35,45.35685,4490.33,0.85,0,1000,0.85,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.3572,0.5,0,0,0,0,31207 +208,0,0,"Office Park High","Office Park High (SACOG 98. Mod Inten. Office)",0.0635,0,0,0,123.370632,0,0,0,0,0,0.95,0,0,0,0.0205,0.0035,0,0.00182,0.02418,0,0,0,0,0,0,0.3,4,1.02,132.18282,13086.1,0.85,0,1000,0.85,500,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.956,0.5,0,0,0,0,31208 +209,0,0,"Office Park High","Office Park High (Bishop Ranch BR-3, San Ramon)",0.0635,0,0,0,85.1598,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,5,0.69,117.98,11680.02,0.85,0,1000,0.85,500,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.7637,0.5,0,0,0,0,31209 +210,0,0,"Office Park High","Office Park High (Bishop Ranch BR-6, San Ramon",0.0635,0,0,0,49.368,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,3,0.4,49.18,4868.82,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.3941,0.5,0,0,0,0,31210 +211,0,0,"Office Park High","Office Park High (SEGA Low Rise Office, 4-6 floors)",0.0635,0,0,0,123.42,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.3,5,1,129.591,12829.51,0.85,0,1000,0.85,500,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.8872,0.5,0,0,0,0,31211 +212,0,0,"Office Park Low","Office Park Low (Redwood Business Park, Petaluma)",0.0635,0,0,0,21.96876,0,0,0,0,0,0.8,0,0,0,0,0,0,0,0,0.2,0,0,0,0,0,0.25,1,0.3,53.99361022,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.5,0.35,0.05,0.1,32212 +213,0,0,"Office Park Low","Office Park Low (Nanometrics Bldg, Milpitas)",0.0635,0,0,0,39.4944,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1,1,0.4,62,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,32213 +214,0,0,"Office Park Low","Office Park Low (Sonoma Technology Bldg, Petaluma)",0.0635,0,0,0,16.456,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5,1,0.2,44.8,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,32214 +215,0,0,"Office Park Low","Office Park Low (Bestronics Bldg, San Jose)",0.0635,0,0,0,29.6208,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.15,1,0.3,43,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,32215 +216,0,0.7,"Industrial High","Industrial High (SEGA Flex R&D, 1-2 floors)",0.0635,0,0,0,16.1087784,0,0,0,0,0,0,0,0,0,0.0082,0.0014,0,0.000728,0.009672,0.98,0,0,0,0,0,0.1,2,0.39,50.54049,0,0.85,0,1000,0.85,750,0.85,500,0.85,900,0.52,0.34,0.07,0.07,1,0.5,0.8,0.2,0,0,33216 +217,0,0,"Industrial High","Industrial High (SACOG 13. Light Indus, 1-2 floors)",0.0635,0,0,0,23.27348571,0,0,0,0,0,0.25,0,0,0,0,0,0,0,0,0.75,0,0,0,0,0,0.15,2,0.33,42.76503,0,0.85,0,1000,0.85,750,0.85,300,0.85,700,0.52,0.34,0.07,0.07,1,0.5,0.8,0.2,0,0,33217 +220,0,0,"Industrial High","Industrial High (SF Produce Markets, San Francisco)",0.0635,0,0,0,28.69515,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0.4,2,0.62,3.67,0,0.85,0,1000,0.85,750,0.85,350,0.85,800,0.52,0.34,0.07,0.07,1,0.5,1,0,0,0,33220 +222,0,0,"Industrial High","Industrial High (Lyons Magnus Plant #1, Fresno)",0.0635,0,0,0,21.752775,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0.3,2,0.47,6.04,0,0.85,0,1000,0.85,750,0.85,350,0.85,800,0.52,0.34,0.07,0.07,1,0.5,0.8,0.2,0,0,33222 +223,0,0,"Industrial High","Industrial High (SCAG Light Indus, 1-2 floors)",0.0635,0,0,0,18.69813,0,0,0,0,0,0,0,0,0,0.0123,0.0021,0,0.001092,0.014508,0.97,0,0,0,0,0,0.05,2,0.5,64.7955,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,1,0.5,0.8,0.2,0,0,33223 +224,0,0.431875,"Industrial Low","Industrial Low (SEGA Heavy Ind, 1-2 floors)",0.0635,0,0,0,10.4907,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0.2,2,0.34,25.17768,0,0.85,0,1000,0.85,750,0.85,400,0.85,1200,0.52,0.34,0.07,0.07,1,0.5,0.8,0.2,0,0,34224 +225,0,0,"Industrial Low","Industrial Low (SACOG 14. Heavy Indus, 1-2 floors)",0.0635,0,0,0,9.4622,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0.05,2,0.23,17.03196,0,0.85,0,1000,0.85,750,0.85,400,0.85,900,0.52,0.34,0.07,0.07,1,0.5,0.8,0.2,0,0,34225 +226,0,0,"Industrial Low","Industrial Low (Pacific Business Center, Fremont CA)",0.0635,0,0,0,11.84832,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0.1,1,0.4,33.8,0,0.85,0,1000,0.85,750,0.85,400,0.85,1250,0.52,0.34,0.07,0.07,1,0.5,0.8,0.2,0,0,34226 +227,0,0,"Industrial Low","Industrial Low (Tank Farm Light Industrial, San Luis Obispo)",0.0635,0,0,0,5.804075676,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0.35,1,0.29,5.08,0,0.85,0,1000,0.85,750,0.85,400,0.85,1850,0.52,0.34,0.07,0.07,1,0.5,0.8,0.2,0,0,34227 +228,0,0,"Industrial Low","Industrial Low (SCAG Heavy Indus, 1-2 floors)",0.0635,0,0,0,15.61263,0,0,0,0,0,0,0,0,0,0.0082,0.0014,0,0.000728,0.009672,0.98,0,0,0,0,0,0.3,2,0.5,37.026,0,0.85,0,1000,0.85,750,0.85,400,0.85,1200,0.52,0.34,0.07,0.07,1,0.5,0.8,0.2,0,0,34228 +229,0,0,"Warehouse High","120 11th Street, San Francisco, CA 94103",0.0635,0,0,0,59.05475136,0,0,0,0,0,0.1547,0,0,0,0,0,0,0,0,0,0.42265,0.42265,0,0,0,0.2,2,1.171927,6.508765365,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35229 +230,0,0,"Warehouse High","1360 Egbert, San Francisco, CA 94124",0.0635,0,0,0,37.026,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5,0.5,0,0,0,0.2,2,1,5.5539,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35230 +231,0,0,"Warehouse High","Dynagraphics - 300 NW 14th Avenue, Portland, OR 97209",0.0635,0,0,0,179.39097,0,0,0,0,0,0.3,0,0,0,0,0,0,0,0,0,0.35,0.35,0,0,0,0.05,2,2.85,17.4,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35231 +232,0,0,"Warehouse High","2181 NW Nicolai, Portland, OR 97210",0.0635,0,0,0,167.431572,0,0,0,0,0,0.3,0,0,0,0,0,0,0,0,0,0.35,0.35,0,0,0,0.05,2,2.66,28,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35232 +233,0,0,"Warehouse High","NW Trunk & Bag Building - 522 N Thompson, Portland, OR 97227",0.0635,0,0,0,85.1598,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5,0.5,0,0,0,0.05,2,2.3,10,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35233 +234,0,0,"Warehouse High","McClaskey Building - 2755 NW 31st Avenue, Portland, OR 97210",0.0635,0,0,0,30.73158,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5,0.5,0,0,0,0.2,2,0.83,21,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35234 +235,0,0,"Warehouse High","525 SE Pine St, Portland, OR 97214",0.0635,0,0,0,167.66607,0,0,0,0,0,0.5,0,0,0,0,0,0,0,0,0,0.25,0.25,0,0,0,0.05,2,2.09,11.607651,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35235 +236,0,0,"Warehouse High","111 SE Madison Ave, Portland, OR 97214",0.0635,0,0,0,141.62445,0,0,0,0,0,0.3,0,0,0,0,0,0,0,0,0,0.35,0.35,0,0,0,0.05,2,2.25,30,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35236 +237,0,0,"Warehouse High","WorkSpace - 2150 Folsom, San Francisco, CA 94110",0.0635,0,0,0,72.94122,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5,0.5,0,0,0,0.1,2,1.97,10.941183,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35237 +238,0,0,"Warehouse High","1154-1158 Howard Street, San Francisco, CA 94103",0.0635,0,0,0,103.6728,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5,0.5,0,0,0,0.05,2,2.8,15.55092,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.15,1,0,0,0,35238 +239,0,0,"Warehouse Low","9040 Carroll Way, San Diego, CA 92121 (Propertyline.com)",0.0635,0,0,0,17.40437985,0,0,0,0,0,0.15,0,0,0,0,0,0,0,0,0,0.425,0.425,0,0,0,0.5,1,0.4339,29,0,0.85,0,1000,0.85,750,0.85,400,0.85,1200,0.52,0.34,0.07,0.07,0.85,0.15,0.4339,0.4433,0,0.1228,36239 +240,0,0,"Warehouse Low","2003 West Avenue 140th, San Leandro, CA 94577 (Loopnet.com)",0.0635,0,0,0,25.49949888,0,0,0,0,0,0.1,0,0,0,0,0,0,0,0,0,0.45,0.45,0,0,0,0.1,1,0.6886917,13.15,0,0.85,0,1000,0.85,750,0.85,400,0.85,1200,0.52,0.34,0.07,0.07,0.85,0.15,0.6768,0.2535,0.0697,0,36240 +241,0,0,"Warehouse Low","2300 Cesar Chavez, San Francisco, CA 94124",0.0635,0,0,0,25.17768,0,0,0,0,0,0.1,0,0,0,0,0,0,0,0,0,0.45,0.45,0,0,0,0.1,1,0.68,8,0,0.85,0,1000,0.85,750,0.85,400,0.85,1200,0.52,0.34,0.07,0.07,0.85,0.15,0.6768,0.2535,0.0697,0,36241 +242,0,0,"Warehouse Low","Warehouse 3 - Proposed Emeryville IKEA (in 1.2/1.6 FAR district)",0.0635,0,0,0,3.0294,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5,0.5,0,0,0,0.3,1,0.45,4.99851,0,0.85,0,1000,0.85,750,0.85,400,0.85,5500,0.52,0.34,0.07,0.07,0.85,0.15,0.6768,0.2535,0.0697,0,36242 +243,0,0,"Hotel High","Hotel High (Four Seasons, San Francisco)",0.065,0,0,0,1151.172,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0.4,40,17.1,248.21,81909.3,0.85,0,1000,0.85,550,0.85,300,0.85,1000,0,0,0,0,0.85,0.5,0.5,0.35,0.05,0.1,37243 +244,0,0,"Hotel High","Hotel High (Walt Disney World Dolphin, Orlando)",0.065,0,0,0,35.39108571,0,0,0,0,0,0.09,0,0,0,0,0,0.91,0,0,0,0,0,0,0,0,0.2,27,0.5,39.6,0,0.85,0,1000,0.85,550,0.85,350,0.85,1000,0,0,0,0,0.85,0.5,0.75,0.1,0.05,0.1,37244 +245,0,0,"Hotel High","Hotel High (Sheraton Grand, Sacramento)",0.065,0,0,0,432.1944,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0.4,27,6.42,87.12,28749.6,0.85,0,1000,0.85,550,0.85,300,0.85,1000,0,0,0,0,0.85,0.5,0.45,0.4,0.05,0.1,37245 +246,0,0,"Hotel Low","Hotel Low (Holiday Inn Express, Truckee)",0.065,0,0,0,67.32,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0.3,3,1,56.27,0,0.85,0,1000,0.85,550,0.85,400,0.85,1000,0,0,0,0,0.85,0.5,0.5,0.35,0.05,0.1,38246 +247,0,0,"Hotel Low","Hotel Low (La Quinta Inn, Redding)",0.065,0,0,0,13.464,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0.3,3,0.2,56.27,0,0.85,0,1000,0.85,550,0.85,400,0.85,1000,0,0,0,0,0.85,0.5,0.75,0.1,0.05,0.1,38247 +248,0,0,"Hotel Low","Hotel Low (Holiday Inn, Woodland Hills)",0.065,0,0,0,44.4312,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0.4,6,0.66,62.41,0,0.85,0,1000,0.85,550,0.85,400,0.85,1000,0,0,0,0,0.85,0.5,0.45,0.4,0.05,0.1,38248 +249,0,0,"Regional Mall","Regional Mall (SEGA General Commerical, 1-2 floors)",0.0635,0,0,0,17.2788,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.1,2,0.35,64.7955,6414.75,0.85,0,1000,0.85,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.5,0.35,0.05,0.1,39249 +250,0,0,"Regional Mall","Regional Mall (SACOG 11. Regional Retail, 1-2 floors)",0.0635,0,0,0,18.044004,0,0,0,0,0,0.05,0,0,0,0.3895,0.0665,0,0.03458,0.45942,0,0,0,0,0,0,0.1,2,0.34,62.9442,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,39250 +251,0,0,"Regional Mall","Regional Mall (Montclair Plaza, San Bernardino)",0.0635,0,0,0,26.65872,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.2,2,0.54,108.64,10755.36,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,39251 +252,0,0,"Regional Mall","Regional Mall (Westfield Galleria, Roseville)",0.0635,0,0,0,24.684,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.25,2,0.5,93.79,9285.21,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,39252 +253,0,0,"Regional Mall","Regional Mall (Westfield Mission Valley, San Diego)",0.0635,0,0,0,26.65872,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.25,1,0.54,65.56,6490.44,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,39253 +254,0,0,"Regional Mall","Regional Mall (SCAG Regional Mall, 1-2 floors)",0.0635,0,0,0,17.2788,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.1,2,0.35,64.7955,0,0.85,0,1000,0.85,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,39254 +255,0,0.343,"Medium Intensity Strip Commercial (weighted avg)","Strip Commercial (SACOG 10. Comm/Nhood Retail, 1-2 floors)",0.0635,0,0,0,13.82304,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.1,2,0.28,51.8364,0,0.85,0,1000,0.85,750,0.85,300,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.5,0.35,0.05,0.1,40255 +256,0,0,"Medium Intensity Strip Commercial (weighted avg)","Med-Intensity Strip Commercial (Plaza Cienega, Los Angeles)",0.0635,0,0,0,13.76543412,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.052,0.468,0,0,0,0,0,0,0.2,1,0.4509,56.6,9339,0.85,0,1000,0.994,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,40256 +257,0,0,"Medium Intensity Strip Commercial (weighted avg)","Med-Intensity Strip Commercial (Greenway Plaza, Yonkers NY)",0.0635,0,0,0,22.2220614,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.104,0.416,0,0,0,0,0,0,0.2,2,0.43727,129.76,12846.24,0.85,0,1000,0.875,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,40257 +258,0,0,"Medium Intensity Strip Commercial (weighted avg)","Med-Intensity Strip Commercial (Tanner Market, Pasadena)",0.0635,0,0,0,56.91490426,0,0,0,0,0,0.045,0,0,0,0.39155,0.06685,0,0.04966,0.44694,0,0,0,0,0,0,0.2,1,0.868,20.16,1995.84,0.85,0,1000,0.933,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,40258 +259,0,0,"Medium Intensity Strip Commercial (weighted avg)","Strip Commercial (SCAG Strip Commerical, 1-2 floors)",0.0635,0,0,0,17.03196,0,0,0,0,0,0.3,0,0,0,0.287,0.049,0,0.02548,0.33852,0,0,0,0,0,0,0.1,2,0.3,55.539,0,0.85,0,1000,0.85,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,40259 +260,0,0,"Medium Intensity Strip Commercial (weighted avg)","Strip Commercial (Cap Metro Strip Commerical, 1-2 floors)",0.0635,0,0,0,12.9591,0,0,0,0,0,0.4,0,0,0,0.246,0.042,0,0.02184,0.29016,0,0,0,0,0,0,0.2,2,0.25,46.2825,0,0.85,0,1000,0.85,1000,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,40260 +261,0,0,"Low Intensity Strip Commercial (weighted avg)","Strip Commercial (Gilroy Crossing, Gilroy)",0.0635,0,0,0,15.30408,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.05,2,0.31,91.74,0,0.85,0,1000,0.85,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.5,0.35,0.05,0.1,41261 +262,0,0,"Low Intensity Strip Commercial (weighted avg)","Strip Commercial (Paso Robles Strip Retail, Paso Robles)",0.0635,0,0,0,12.342,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.1,1,0.25,95.17,0,0.85,0,1000,0.85,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,41262 +263,0,0,"Low Intensity Strip Commercial (weighted avg)","Strip Commercial (Renaissance Center West, Las Vegas)",0.0635,0,0,0,13.99540473,0,0,0,0,0,0.017,0,0,0,0.40303,0.06881,0,0.25558,0.25558,0,0,0,0,0,0,0.15,1,0.2405,57.19,0,0.85,0,1000,0.994,750,0.994,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,41263 +264,0,0,"Low Intensity Strip Commercial (weighted avg)","Strip Commercial (Mission Viejo Commerce Center)",0.0635,0,0,0,12.1968,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.1,1,0.21,41.75,0,0.85,0,1000,1,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,41264 +266,0,0,"Low Intensity Strip Commercial (weighted avg)","Strip Commercial (Guernville Rd McDonald's, Santa Rosa CA)",0.0635,0,0,0,5.808,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.4,1,0.1,26.7,0,0.85,0,1000,1,750,0.99,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,41266 +267,0,0,"Low Intensity Strip Commercial (weighted avg)","Strip Commercial (Stanford Ranch, Roseville)",0.0635,0,0,0,10.86096,0,0,0,0,0,0,0,0,0,0.41,0.07,0,0.0364,0.4836,0,0,0,0,0,0,0.2,1,0.22,103.13,0,0.85,0,1000,0.85,750,0.85,500,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,41267 +269,0,0,"Rural Employment","Occidential Elk Hills Oil Field",0.031,0,0,0,0.004,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0.05,1,0.00009,0.0029403,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0001,0,0,0,0,0,42269 +272,0,0,"Rural Employment","Large Farm (Near Watsonville)",0.031,3.06,0,0.00018513,0.00646866,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.99,0,0.05,1,0.0005,0.01635678,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0006,0.95,0,0,0,0,42272 +273,0,0,"Rural Employment","Mid-sized farm 1300x1300 (near Manteca)",0.031,3.06,0,0.00074052,0.02587464,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.99,0,0.03,1,0.002,0.06542712,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0025,0.95,0,0,0,0,42273 +274,0,0,"Rural Employment","Small farm 1300x650 (near Modesto)",0.031,3.06,0,0.00222156,0.03841992,0.02,0,0,0,0,0,0,0,0,0,0,0,0,0,"1.11022E-16",0,0,0,0.98,0,0.02,1,0.003,0.09827136,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0037,0.95,0,0,0,0,42274 +275,0,0,"Rural Employment","Very Small Farm 650x650 (Near Modesto)",0.031,3.06,0,0.00055539,0.00633798,0.03,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.97,0,0.01,1,0.0005,0.01640034,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0006,0.95,0,0,0,0,42275 +277,0,0,"Rural Employment","Very Large Orchard (Near Tracy)",0.031,0,0,0,0.006534,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0.05,1,0.0005,0.016335,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0006,0.95,0,0,0,0,42277 +278,0,0,"Rural Employment","Medium Orchard (Near Tracy)",0.031,0,0,0,0.006534,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0.03,1,0.0005,0.016335,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0006,0.95,0,0,0,0,42278 +279,0,0,"Rural Employment","Small Orchard (Near Ojai)",0.031,3.06,0,0.00018513,0.00646866,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.99,0,0.02,1,0.0005,0.01635678,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0006,0.95,0,0,0,0,42279 +281,0,0,"Rural Employment","Large Organic Farm (Frog Hollow, Brentwood)",0.031,3.06,0,0.00018513,0.00646866,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.99,0,0.03,1,0.0005,0.01635678,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0006,0.95,0,0,0,0,42281 +282,0,0,"Rural Employment","Medium Organic Farm (Live Power Farm, Covelo)",0.031,3.06,0,0.00037026,0.00640332,0.02,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.98,0,0.02,1,0.0005,0.01637856,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0006,0.95,0,0,0,0,42282 +283,0,0,"Rural Employment","Small Organic Farm (Gospel Flat Farm, Bollinas)",0.031,3.06,0,0.00055539,0.00633798,0.03,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.97,0,0.01,1,0.0005,0.01640034,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0006,0.95,0,0,0,0,42283 +285,0,0,"Rural Employment","Livestock Farm: Grassfed beef (Chilleno Valley Ranch, Petaluma)",0.031,0,0,0,0.00013068,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0.27,1,0.00001,0.0003267,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0,0.95,0,0,0,0,42285 +286,0,0,"Rural Employment","Livestock Farm: Harris Ranch Feedlot (I-5 CA-145 Interchange)",0.031,0,0,0,0.00013068,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0.2,1,0.00001,0.0003267,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0,0.95,0,0,0,0,42286 +288,0,0,"Rural Employment","Vineyard, Small (Martin Stelling Vineyard)",0.031,3.06,0,0.000111078,0.001267596,0.03,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.97,0,0.05,1,0.0001,0.003280068,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0001,0.95,0,0,0,0,42288 +289,0,0,"Rural Employment","Vineyward, Medium (Quintessa Vineyard)",0.031,3.06,0,0.000074052,0.001280664,0.02,0,0,0,0,0,0,0,0,0,0,0,0,0,"1.11022E-16",0,0,0,0.98,0,0.05,1,0.0001,0.003275712,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0001,0.95,0,0,0,0,42289 +290,0,"http://www.argentinevineyardsforsale.com/own.html?adore_category=1","Rural Employment","Vineyard, Large (Napa Valley Wine Company)",0.031,3.06,0,0.000037026,0.001293732,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.99,0,0.05,1,0.0001,0.003271356,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0001,0.95,0,0,0,0,42290 +294,0,0,"Rural Employment","Liberty Quarry Proposal (Temecula, CA)",0.031,0,0,0,0.0013068,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0.05,1,0.0001,0.003267,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0001,0,0,0,0,0,42294 +296,0,"http://www.mauinews.com/page/content.detail/id/527970.html?nav=10","Rural Employment","Castle & Cook Resorts Wind Farm Proposal (Lanai, HI)",0.031,0,0,0,0.169884,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0.01,1,0.013,0.42471,0,0.85,20000,1000,0.85,750,0.85,500,0.75,2500,0.52,0.34,0.07,0.07,0.0162,0,0,0,0,0,42296 +298,0,0,"Campus/College High","Campus/College High (LA City College, Los Angeles)",0.0635,0,0,0,35.1747,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0.57,63.8,21054,0.85,0,1000,0.85,750,0.85,600,0.85,1000,0,0,0,0,0.85,0.9,0.75,0.1,0.05,0.1,43298 +300,0,0,"Campus/College Low","Campus/College Low",0.0635,0,0,0,17.08892308,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0.3,22.2156,0,0.85,0,1000,0.85,750,0.85,650,0.85,1000,0,0,0,0,0.85,0.9,0.5,0.35,0.05,0.1,44300 +304,0,0,"Hospital/Civic/Other Institutional","Hospital (Children's Hospital, Los Angeles)",0.0232,0,0,0,175.355136,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,9,2.96,176.85,58360.5,0.85,0,1000,0.85,750,0.85,625,0.85,1000,0,0,0,0,0.85,0.9,0.75,0.1,0.05,0.1,45304 +307,"Moor Ruble Yudell",0,"Urban Elementary School","Urban Elementary School (Horace Mann ES, San Jose)",0,0,0,0,4.44312,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0.3,1.11078,0,0.85,0,1000,0.85,1250,0.85,2500,0.85,1000,0,0,0,0,0.85,0.9,0.75,0.1,0.05,0.1,46307 +310,0,0,"Non-Urban Elementary School","Non-Urban Elementary School",0,0,0,0,2.6928,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0.2,3.7026,0,0.85,0,1000,0.85,1250,0.85,2750,0.85,1000,0,0,0,0,0.85,0.9,0.5,0.35,0.05,0.1,47310 +315,0,0,"Urban Middle School","Central Los Angeles Middle School",0,0,0,0,4.9368,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0.4,1.48104,0,0.85,0,1000,0.994,1120,0.85,3000,0.85,1000,0,0,0,0,0.85,0.9,0.45,0.4,0.05,0.1,48315 +316,0,0,"Non-Urban Middle School","Non-Urban Middle School",0,0,0,0,1.97472,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0.2,3.7026,0,0.85,0,1000,0.85,1250,0.85,3750,0.85,1000,0,0,0,0,0.85,0.9,0.5,0.35,0.05,0.1,49316 +320,0,0,"Urban High School","Urban High School (Berkeley High School, Berkeley CA)",0,0,0,0,5.92416,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0.6,5.5,1815,0.85,0,1000,0.85,1250,0.85,3750,0.85,1000,0,0,0,0,0.85,0.9,0.75,0.1,0.05,0.1,50320 +322,0,0,"Non-Urban High School","Non-Urban High School",0,0,0,0,1.8513,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0.25,4.62825,0,0.85,0,1000,0.85,1250,0.85,5000,0.85,1000,0,0,0,0,0.85,0.9,0.5,0.35,0.05,0.1,51322 +326,0,0,"Urban City Hall","Urban City Hall (Oakland City Hall, Oakland, CA)",0.0635,0,0,0,17.77248,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.2,12,0.18,0.00666468,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,52326 +327,0,0,"Urban City Hall","Urban City Hall (Long Beach City Hall and Civic Center, Long Beach, CA)",0.0635,0,0,0,37.27284,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.8,2,0.453,0.016772778,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,52327 +329,0,0,"Urban Public Library","Urban Public Library - Main Branch (Oakland Public Library, Oakland, CA)",0.0635,0,0,0,39.691872,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,2,0.402,0.014884452,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,53329 +332,0,0,"Urban Courthouse","Urban Courthouse (Rene C. Davidson Courthouse, Oakland, CA)",0.0635,0,0,0,96.7415328,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0.4,6,0.71,0.036278075,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,54332 +333,0,0,"Urban Courthouse","Urban Courthouse (Long Beach Superior Court, Long Beach, CA)",0.0635,0,0,0,22.13332,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0.6,10,0.269,0.009959994,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,54333 +334,0,0,"Urban Convention Center","Urban Convention Center (Oakland Convention Center, Oakland, CA)",0.0635,0,0,0,78.9888,0,0,0,0,0,0,0,0,0.9,0.041,0.007,0,0.00364,0.04836,0,0,0,0,0,0,0.75,3,1,0.037026,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.5,0.35,0.05,0.1,55334 +335,0,0,"Urban Convention Center","Urban Convention Center (Long Beach Convention and Entertainment Center, Long Beach, CA)",0.0635,0,0,0,23.4596736,0,0,0,0,0,"-1.11022E-16",0,0,0.8,0.082,0.014,0,0.00728,0.09672,0,0,0,0,0,0,0.25,3,0.264,0.009774864,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,55335 +337,0,0,"Suburban Civic Complex","Suburban Civic Complex (City Hall, Library, Rec Center, Menlo Park, CA)",0.0635,0,0,0,7.8166,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0.4,3,0.095,3.51747,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.5,0.35,0.05,0.1,56337 +338,0,0,"Suburban Civic Complex","Suburban Civic Complex (City Hall, Library, Gym and Teen Center, Walnut, CA)",0.0635,0,0,0,19.845936,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0.3,3,0.201,7.442226,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,56338 +339,0,0,"Suburban Civic Complex","Suburban Civic Buildings (Police Station, Community Services, Walnut, CA)",0.0635,0,0,0,11.5192,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0.3,2,0.14,5.18364,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,56339 +341,0,0,"Town Civic Complex","Town Civic Complex (City Hall, Police Dept., St. Helena, CA)",0.0635,0,0,0,21.72192,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0.5,2,0.22,8.14572,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,57341 +342,0,0,"Town Civic Complex","Town Civic Complex (City Hall, Police and Fire Dept., Bishop, CA)",0.0635,0,0,0,30.855,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0.5,2,0.375,13.88475,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,57342 +345,0,0,"Town/Branch Library","Town Library (St Helena Public Library, St. Helena, CA)",0.0635,0,0,0,34.54436938,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0.5,1,0.342,12.95413852,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,58345 +346,0,0,"Town/Branch Library","Town Library (Bishop Branch Library, Bishop, CA)",0.0635,0,0,0,52.6592,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0.5,2,0.64,23.69664,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,58346 +349,0,0,"Church","Church 1",0.0635,0,0,0,33.68076014,0,0,0,0,0,0.95,0,0,0,0.0205,0.0035,0,0.00182,0.02418,0,0,0,0,0,0,0.5,2,0.342,12.95413852,0,0.85,0,1000,0.85,750,0.85,375,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.75,0.1,0.05,0.1,59349 +350,0,0,"Church","Church 2",0.0635,0,0,0,51.606016,0,0,0,0,0,0.95,0,0,0,0.0205,0.0035,0,0.00182,0.02418,0,0,0,0,0,0,0.5,1,0.64,23.69664,0,0.85,0,1000,0.85,750,0.85,450,0.85,1000,0.52,0.34,0.07,0.07,0.85,0.5,0.45,0.4,0.05,0.1,59350 diff --git a/footprint/client/configuration/default/built_form/import_csv/buildingtypes.csv b/footprint/client/configuration/default/built_form/import_csv/buildingtypes.csv new file mode 100644 index 000000000..24b8da404 --- /dev/null +++ b/footprint/client/configuration/default/built_form/import_csv/buildingtypes.csv @@ -0,0 +1,63 @@ +"meta_class","btid","color_hex","building_type","urban_mixed_use","urban_residential","urban_commercial","city_mixed_use","city_residential","city_commercial","town_mixed_use","town_residential","town_commercial","village_mixed_use","village_residential","village_commercial","neighborhood_residential","neighborhood_low","office_focus","mixed_office_and_r_and_d","office_industrial","industrial_focus","low_density_employment_park","high_intensity_activity_center","mid_intensity_activity_center","low_intensity_retail_centered_neighborhood","retail_strip_mall_big_box","industrial_office_res_mixed_high","industrial_office_res_mixed_low","suburban_multifamily","suburban_mixed_residential","residential_subdivision","large_lot_residential_area","rural_residential","rural_ranchettes","rural_employment","campus_or_university","institutional","parks_and_open_space" +"Mixed Use",1,"#940000","Skyscraper Mixed Use",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Mixed Use",2,"#940000","High-Rise Mixed Use",0.1259119205,0,0.0108729536,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Mixed Use",3,"#C20000","Mid-Rise Mixed Use",0.1007295364,0,0.0108729536,0.0765,0.0102,0.0055,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Mixed Use",4,"#E61414","Low-Rise Mixed Use",0.0604377218,0,0.0163094305,0.0714,0,0.0055,0.048,0,0.011,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Mixed Use",5,"#FF6699","Parking Structure/Mixed Use",0,0,0,0.0255,0,0.0055,0.024,0,0.0165,0,0,0,0,0,0,0,0,0,0,0.0346284967,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Mixed Use",6,"#FF5252","Main Street Commercial/MU High (3-5 Floors)",0.0251823841,0.0251823841,0.0271823841,0.051,0.0153,0.0385,0.0624,0,0.0275,0.0245099904,0,0,0,0,0,0,0,0,0,0.2770279732,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Mixed Use",7,"#FF5252","Main Street Commercial/MU Low (1-2 Floors)",0,0,0,0,0,0,0.048,0.0705387053,0.055,0.0735299713,0,0.0550199809,0.0115598247,0,0,0,0,0,0,0,0.0346284967,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Residential",8,"#CC5500","Skyscraper Residential",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Residential",9,"#CC5500","High-Rise Residential",0.0755471523,0.2266414569,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Residential",10,"#E66A05","Urban Mid-Rise Residential",0.0251823841,0.1762766887,0,0.0765,0.2244,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1625692706,0,0,0,0,0,0,0,0,0.1208381754,0,0 +"Residential",11,"#FF901A","Urban Podium Multi-Family",0.0151094305,0.0503647682,0,0.051,0.1326,0,0.048,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0774139384,0,0,0,0,0,0,0,0,0.0671323196,0.0112352538,0 +"Residential",12,"#FF901A","Standard Podium Multi-Family",0,0,0,0,0.051,0,0.024,0.1128619285,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0851553322,0,0.227313635,0,0,0,0,0,0,0.0671323196,0,0 +"Residential",13,"#FF901A","Suburban Multifamily Apt/Condo",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0346284967,0.0346284967,0.013039207,0,0.0696725446,0.0764139384,0.2597870114,0.1198935057,0,0,0,0,0,0,0,0 +"Residential",14,"#FFA733","Urban Townhome/Live-Work",0,0.0201459073,0,0.051,0.0765,0,0.096,0.2539393391,0,0.1470599426,0.2981423404,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Residential",15,"#FFA733","Standard Townhome",0,0,0,0,0,0,0,0.0564309642,0,0,0,0,0,0,0,0,0,0,0,0.0692569933,0.0346284967,0,0,0.0851553322,0.1146209076,0.1298935057,0.0599467528,0,0,0,0,0,0,0,0 +"Residential",16,"#FFA733","Garden Apartment",0,0,0,0,0,0,0,0.0705387053,0,0,0,0,0,0,0,0,0,0,0,0,0.10388549,0.0195588104,0,0,0.0840553322,0.0324733764,0.0899201293,0,0,0,0,0,0,0,0 +"Residential",17,"#FCFE6F","Very Small Lot 3000",0,0,0,0,0,0,0,0,0,0.0490199809,0.1897269439,0,0.3930340394,0,0,0,0,0,0,0,0,0,0,0,0.0382069692,0,0.0299733764,0,0,0,0,0,0,0,0 +"Residential",18,"#FCFE6F","Small Lot 4000",0,0,0,0,0,0,0,0,0,0.0980399617,0.0542076982,0,0.1444978086,0.2311964938,0,0,0,0,0,0,0,0.0977940521,0,0,0.0305655754,0,0.1079041551,0.199313635,0,0,0,0,0,0.0224705075,0 +"Residential",19,"#FCFE6F","Medium Lot 5500",0,0,0,0,0,0,0,0,0,0,0,0,0.0288995617,0.2311964938,0,0,0,0,0,0,0,0.1825488973,0,0,0,0,0.0899201293,0.2847337642,0,0,0,0,0,0,0 +"Residential",20,"#FFFFCC","Large Lot 7500",0,0,0,0,0,0,0,0,0,0,0,0,0,0.1155982469,0,0,0,0,0,0,0,0.0651960348,0,0,0,0,0.0719361034,0.0284733764,0.2702515788,0,0,0,0,0,0 +"Residential",21,"#FFFFCC","Estate Lot",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0284733764,0.2026886841,0.0424978909,0,0,0,0,0 +"Residential",22,"#FFFFCC","Rural Residential",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.1824198157,0.3824810181,0,0,0,0,0 +"Residential",23,"#EBE07A","Rural Ranchette",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.4164793308,0.9167888889,0.046874093,0,0,0 +"Office/Industrial",24,"#DCC156","Skyscraper Office",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Office/Industrial",25,"#5D2682","High-Rise Office",0.0251823841,0,0.1630943046,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Office/Industrial",26,"#862C90","Mid-Rise Office",0,0,0.1359119205,0.0255,0,0.099,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Office/Industrial",27,"#8764A6","Low-Rise Office",0,0,0.1250389669,0.0255,0,0.1375,0.0912,0,0.0825,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0619311507,0,0,0,0,0,0,0,0,0,0.0674115226,0 +"Commercial/Retail",28,"#DC41B8","Main Street Commercial (Retail + Office/Medical)",0,0,0,0,0,0.1925,0.024,0,0.275,0.0735299713,0,0.440159847,0,0,0,0,0,0,0,0.1731424833,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Commercial/Retail",29,"#F1C3C3","Parking Structure+Ground-Floor Retail",0.0251823841,0,0.0271823841,0.0306,0,0.0275,0,0,0.0275,0,0,0,0,0,0,0,0,0,0,0.0692569933,0.0692569933,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Commercial/Retail",30,"#A6A6A6","Parking Structure",0,0,0,0,0,0,0,0,0.0275,0,0,0,0,0,0,0.0349448517,0,0,0,0,0.0692569933,0,0,0,0,0,0,0,0,0,0,0,0.0134264639,0.0224705075,0 +"Office/Industrial",31,"#A6A6A6","Office Park High",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.5940624783,0.2166580803,0.037822314,0,0,0,0,0,0,0.0774139384,0,0,0,0,0,0,0,0,0,0,0 +"Office/Industrial",32,"#EEE5FF","Office Park Low",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.230636021,0,0,0.028,0,0,0,0,0,0.0764139384,0,0,0,0,0,0,0,0,0,0 +"Office/Industrial",33,"#EEE5FF","Industrial High",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0559117627,0.6051570248,0.0372678344,0,0,0,0,0,0.0774139384,0.0764139384,0,0,0,0,0,0,0,0,0.0112352538,0 +"Office/Industrial",34,"#E6CCFF","Industrial Low",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.020966911,0,0.1490713377,0.112,0,0,0,0,0,0.0764139384,0,0,0,0,0,0,0,0,0.0112352538,0 +"Office/Industrial",35,"#E6CCFF","Warehouse High",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0698897033,0.0489227923,0.037822314,0.0745356689,0,0,0,0,0,0.0696725446,0.1833934521,0,0,0,0,0,0,0,0,0.0112352538,0 +"Office/Industrial",36,"#8764A6","Warehouse Low",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.020966911,0,0.3354105099,0.392,0,0,0,0,0.0077413938,0.0076413938,0,0,0,0,0,0,0,0,0.0112352538,0 +"Commercial/Retail",37,"#E6CCFF","Hotel High",0.0251823841,0.0050364768,0.0271823841,0.0153,0,0.0275,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0346284967,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Commercial/Retail",38,"#F679AB","Hotel Low",0,0,0,0.0102,0,0.011,0.0144,0,0.0165,0.0245099904,0,0.0275099904,0,0,0,0,0,0,0,0,0.0346284967,0.013039207,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Commercial/Retail",39,"#FF9B99","Regional Mall",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.10388549,0,0.1146209076,0,0,0,0,0,0,0,0,0,0,0,0 +"Commercial/Retail",40,"#FFB0AD","Medium Intensity Strip Commercial (weighted avg)",0,0,0,0,0,0,0,0,0.011,0,0,0.0165059943,0,0,0.0349448517,0.0349448517,0,0,0,0,0,0.0325980174,0.382069692,0,0,0,0,0,0,0,0,0,0,0.0112352538,0 +"Commercial/Retail",41,"#F679AB","Low Intensity Strip Commercial (weighted avg)",0,0,0,0,0,0,0,0,0,0,0,0.0110039962,0,0,0,0.0349448517,0.0756446281,0.1490713377,0.028,0,0.2077709799,0.2281861216,0.2674487844,0,0,0,0.0299733764,0.0284733764,0.0202688684,0,0,0,0,0.0112352538,0 +"Office/Industrial",42,"#FFB0AD","Rural Employment",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0092604938,0.8906077667,0,0.0337057613,0.0005249866 +"Institutional",43,"#365F91","Campus/College High",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.4,0,0 +"Institutional",44,"#365F91","Campus/College Low",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Institutional",45,"#538DD5","Hospital/Civic/Other Institutional",0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.02,0.01,0.01,0,0,0,0,0,0,0,0,0,0,0,0.02,0.02,0.02,0.02,0,0,0,0.02,0.4,0 +"Civic",46,"#538DD5","Urban Elementary School",0.02,0.02,0,0.02,0.02,0,0.04,0.04,0,0.04,0.04,0,0.02,0.02,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",47,"#538DD5","Non-Urban Elementary School",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.03,0,0,0,0.02,0.03,0.06,0.04,0,0,0,0,0,0 +"Civic",48,"#538DD5","Urban Middle School",0.01,0.01,0,0.01,0.01,0,0.02,0.02,0,0.02,0.02,0,0.02,0.02,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",49,"#538DD5","Non-Urban Middle School",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.03,0,0,0,0.01,0.03,0.03,0.02,0,0,0,0,0,0 +"Civic",50,"#538DD5","Urban High School",0.01,0.01,0,0.01,0.01,0,0.01,0.01,0,0.01,0.01,0,0.01,0.01,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",51,"#538DD5","Non-Urban High School",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.03,0,0,0,0.01,0.03,0.03,0.02,0.0009,0,0,0,0,0 +"Infrastructure",0,"#A6A6A6","Detention/Utilities",0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.01,0.05,0.05,0.03,0.05,0.05,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.04,0.02,0.02,0.02,0.02,0.02 +"Infrastructure",0,"#7DBD4C","Park",0.07,0.07,0.07,0.07,0.07,0.07,0.07,0.07,0.07,0.1,0.1,0.1,0.1,0.1,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.04,0.03,0.04,0.04,0.04,0.04,0.04,0.01,0.01,0.01,0.1,0.1,0.871 +"Infrastructure",0,,"Streets",0.366352318,0.366352318,0.366352318,0.36,0.36,0.36,0.36,0.2756903576,0.36,0.3198001913,0.2679230175,0.3198001913,0.2520087655,0.2520087655,0.2111029667,0.2111029667,0.173553719,0.1646433114,0.35,0.247430067,0.247430067,0.1980396525,0.1758606161,0.1758606161,0.1758606161,0.2305324715,0.2305324715,0.2305324715,0.164371053,0.0991421821,0.0439506173,0.0325181403,0.1914707214,0.2552949246,0.1084750134 +"Civic",52,"#538DD5","Urban City Hall",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",53,"#538DD5","Urban Public Library",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",54,"#538DD5","Urban Courthouse",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",55,"#538DD5","Urban Convention Center",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",56,"#538DD5","Suburban Civic Complex",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",57,"#538DD5","Town Civic Complex",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",58,"#538DD5","Town/Branch Library",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +"Civic",59,"#538DD5","Church",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 diff --git a/footprint/client/configuration/default/built_form/import_csv/placetypes.csv b/footprint/client/configuration/default/built_form/import_csv/placetypes.csv new file mode 100644 index 000000000..4f469471e --- /dev/null +++ b/footprint/client/configuration/default/built_form/import_csv/placetypes.csv @@ -0,0 +1,35 @@ +Urban Mixed Use,urban_mixed_use,#940000,200 +Urban Residential,urban_residential,#CC5500,200 +Urban Commercial,urban_commercial,#5D2682,200 +City Mixed Use,city_mixed_use,#C20000,200 +City Residential,city_residential,#E66A05,200 +City Commercial,city_commercial,#862C90,200 +Town Mixed Use,town_mixed_use,#E61414,200 +Town Residential,town_residential,#FA7E0A,220 +Town Commercial,town_commercial,#B12F9C,200 +Village Mixed Use,village_mixed_use,#FF5252,220 +Village Residential,village_residential,#FF901A,180 +Village Commercial,village_commercial,#DC41B8,230 +Neighborhood Residential,neighborhood_residential,#FFA733,180 +Neighborhood Low,neighborhood_low,#FFBA42,230 +Office Focus,office_focus,#8764A6,45 +Mixed Office and R&D,mixed_office_and_r_and_d,#A981D5,45 +Office/Industrial,office_industrial,#CF9DEC,40 +Industrial Focus,industrial_focus,#E6CCFF,35 +Low-Density Employment Park,low_density_employment_park,#EEE5FF,35 +High Intensity Activity Center,high_intensity_activity_center,#F679AB,130 +Mid Intensity Activity Center,mid_intensity_activity_center,#FF9B99,70 +Low Intensity Retail-Centered N'Hood,low_intensity_retail_centered_neighborhood,#FFB0AD,65 +Retail: Strip Mall/ Big Box,retail_strip_mall_big_box,#FFCCCC,60 +Industrial/Office/Res Mixed High,industrial_office_residential_mixed_high,#FBC781,60 +Industrial/Office/Res Mixed Low,industrial_office_residential_mixed_low,#FCDBAE,60 +Suburban Multifamily,suburban_multifamily,#FECF58,90 +Suburban Mixed Residential,suburban_mixed_residential,#FEE567,90 +Residential Subdivision,residential_subdivision,#FEF295,90 +Large Lot Residential Area,large_lot_residential,#FEF6A9,20 +Rural Residential,rural_residential,#EBE07A,15 +Rural Ranchettes,rural_ranchettes,#DCC156,10 +Rural Employment,rural_employment,#BCD397,10 +Campus/ University,campus_or_university,#365F91,150 +Institutional,institutional,#538DD5,130 +Parks & Open Space,parks_and_open_space,#7DBD4C,50 \ No newline at end of file diff --git a/footprint/client/configuration/default/built_form/json_fixtures/building_percents.json b/footprint/client/configuration/default/built_form/json_fixtures/building_percents.json new file mode 100644 index 000000000..83bdedae1 --- /dev/null +++ b/footprint/client/configuration/default/built_form/json_fixtures/building_percents.json @@ -0,0 +1,21141 @@ +[ + { + "pk": 8, + "model": "main.tag", + "fields": { + "tag": "Unsorted" + } + }, + { + "pk": 6, + "model": "main.tag", + "fields": { + "tag": "Commercial/Retail" + } + }, + { + "pk": 1, + "model": "main.tag", + "fields": { + "tag": "Mixed Use" + } + }, + { + "pk": 2, + "model": "main.tag", + "fields": { + "tag": "Civic" + } + }, + { + "pk": 3, + "model": "main.tag", + "fields": { + "tag": "Institutional" + } + }, + { + "pk": 4, + "model": "main.tag", + "fields": { + "tag": "Office/Industrial" + } + }, + { + "pk": 5, + "model": "main.tag", + "fields": { + "tag": "Residential" + } + }, + { + "pk": 7, + "model": "main.tag", + "fields": { + "tag": "unused" + } + }, + { + "pk": 10, + "model": "main.medium", + "fields": { + "key": "built_form_6", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzM2NUY5MXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 11, + "model": "main.medium", + "fields": { + "key": "built_form_7", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 12, + "model": "main.medium", + "fields": { + "key": "built_form_8", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 15, + "model": "main.medium", + "fields": { + "key": "built_form_12", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 16, + "model": "main.medium", + "fields": { + "key": "built_form_13", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 17, + "model": "main.medium", + "fields": { + "key": "built_form_14", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 18, + "model": "main.medium", + "fields": { + "key": "built_form_15", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 19, + "model": "main.medium", + "fields": { + "key": "built_form_16", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 20, + "model": "main.medium", + "fields": { + "key": "built_form_17", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 21, + "model": "main.medium", + "fields": { + "key": "built_form_18", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 22, + "model": "main.medium", + "fields": { + "key": "built_form_19", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 23, + "model": "main.medium", + "fields": { + "key": "built_form_20", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 26, + "model": "main.medium", + "fields": { + "key": "built_form_23", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 27, + "model": "main.medium", + "fields": { + "key": "built_form_24", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 28, + "model": "main.medium", + "fields": { + "key": "built_form_25", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 29, + "model": "main.medium", + "fields": { + "key": "built_form_26", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 30, + "model": "main.medium", + "fields": { + "key": "built_form_27", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 31, + "model": "main.medium", + "fields": { + "key": "built_form_28", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 32, + "model": "main.medium", + "fields": { + "key": "built_form_29", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 33, + "model": "main.medium", + "fields": { + "key": "built_form_30", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 35, + "model": "main.medium", + "fields": { + "key": "built_form_33", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 36, + "model": "main.medium", + "fields": { + "key": "built_form_34", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 37, + "model": "main.medium", + "fields": { + "key": "built_form_35", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 38, + "model": "main.medium", + "fields": { + "key": "built_form_36", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 41, + "model": "main.medium", + "fields": { + "key": "built_form_39", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 42, + "model": "main.medium", + "fields": { + "key": "built_form_40", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 43, + "model": "main.medium", + "fields": { + "key": "built_form_41", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 44, + "model": "main.medium", + "fields": { + "key": "built_form_42", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 45, + "model": "main.medium", + "fields": { + "key": "built_form_43", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 47, + "model": "main.medium", + "fields": { + "key": "built_form_45", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 48, + "model": "main.medium", + "fields": { + "key": "built_form_46", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 49, + "model": "main.medium", + "fields": { + "key": "built_form_47", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 50, + "model": "main.medium", + "fields": { + "key": "built_form_49", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 51, + "model": "main.medium", + "fields": { + "key": "built_form_50", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 54, + "model": "main.medium", + "fields": { + "key": "built_form_53", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 56, + "model": "main.medium", + "fields": { + "key": "built_form_55", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 57, + "model": "main.medium", + "fields": { + "key": "built_form_56", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 58, + "model": "main.medium", + "fields": { + "key": "built_form_57", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 59, + "model": "main.medium", + "fields": { + "key": "built_form_58", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 60, + "model": "main.medium", + "fields": { + "key": "built_form_59", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 61, + "model": "main.medium", + "fields": { + "key": "built_form_60", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 62, + "model": "main.medium", + "fields": { + "key": "built_form_61", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 63, + "model": "main.medium", + "fields": { + "key": "built_form_62", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 64, + "model": "main.medium", + "fields": { + "key": "built_form_63", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 65, + "model": "main.medium", + "fields": { + "key": "built_form_64", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 66, + "model": "main.medium", + "fields": { + "key": "built_form_66", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 67, + "model": "main.medium", + "fields": { + "key": "built_form_67", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 69, + "model": "main.medium", + "fields": { + "key": "built_form_69", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 70, + "model": "main.medium", + "fields": { + "key": "built_form_70", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 71, + "model": "main.medium", + "fields": { + "key": "built_form_71", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 72, + "model": "main.medium", + "fields": { + "key": "built_form_72", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 73, + "model": "main.medium", + "fields": { + "key": "built_form_73", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 74, + "model": "main.medium", + "fields": { + "key": "built_form_74", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 75, + "model": "main.medium", + "fields": { + "key": "built_form_75", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 76, + "model": "main.medium", + "fields": { + "key": "built_form_76", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 77, + "model": "main.medium", + "fields": { + "key": "built_form_77", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 79, + "model": "main.medium", + "fields": { + "key": "built_form_79", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 80, + "model": "main.medium", + "fields": { + "key": "built_form_80", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 81, + "model": "main.medium", + "fields": { + "key": "built_form_81", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 83, + "model": "main.medium", + "fields": { + "key": "built_form_83", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 84, + "model": "main.medium", + "fields": { + "key": "built_form_84", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 85, + "model": "main.medium", + "fields": { + "key": "built_form_85", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 86, + "model": "main.medium", + "fields": { + "key": "built_form_87", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 87, + "model": "main.medium", + "fields": { + "key": "built_form_88", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 88, + "model": "main.medium", + "fields": { + "key": "built_form_89", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 89, + "model": "main.medium", + "fields": { + "key": "built_form_90", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 90, + "model": "main.medium", + "fields": { + "key": "built_form_91", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 91, + "model": "main.medium", + "fields": { + "key": "built_form_92", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 92, + "model": "main.medium", + "fields": { + "key": "built_form_93", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 93, + "model": "main.medium", + "fields": { + "key": "built_form_94", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 94, + "model": "main.medium", + "fields": { + "key": "built_form_95", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 95, + "model": "main.medium", + "fields": { + "key": "built_form_96", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 96, + "model": "main.medium", + "fields": { + "key": "built_form_97", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 97, + "model": "main.medium", + "fields": { + "key": "built_form_99", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 98, + "model": "main.medium", + "fields": { + "key": "built_form_100", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 99, + "model": "main.medium", + "fields": { + "key": "built_form_101", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 100, + "model": "main.medium", + "fields": { + "key": "built_form_102", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 101, + "model": "main.medium", + "fields": { + "key": "built_form_103", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 102, + "model": "main.medium", + "fields": { + "key": "built_form_104", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 103, + "model": "main.medium", + "fields": { + "key": "built_form_105", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 104, + "model": "main.medium", + "fields": { + "key": "built_form_106", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 109, + "model": "main.medium", + "fields": { + "key": "built_form_111", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 110, + "model": "main.medium", + "fields": { + "key": "built_form_112", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 114, + "model": "main.medium", + "fields": { + "key": "built_form_117", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 115, + "model": "main.medium", + "fields": { + "key": "built_form_118", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 116, + "model": "main.medium", + "fields": { + "key": "built_form_119", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 117, + "model": "main.medium", + "fields": { + "key": "built_form_120", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 118, + "model": "main.medium", + "fields": { + "key": "built_form_121", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 119, + "model": "main.medium", + "fields": { + "key": "built_form_122", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 120, + "model": "main.medium", + "fields": { + "key": "built_form_123", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 121, + "model": "main.medium", + "fields": { + "key": "built_form_124", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 122, + "model": "main.medium", + "fields": { + "key": "built_form_125", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 126, + "model": "main.medium", + "fields": { + "key": "built_form_131", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 127, + "model": "main.medium", + "fields": { + "key": "built_form_132", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 129, + "model": "main.medium", + "fields": { + "key": "built_form_134", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 130, + "model": "main.medium", + "fields": { + "key": "built_form_135", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 131, + "model": "main.medium", + "fields": { + "key": "built_form_136", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 133, + "model": "main.medium", + "fields": { + "key": "built_form_138", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 134, + "model": "main.medium", + "fields": { + "key": "built_form_139", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 136, + "model": "main.medium", + "fields": { + "key": "built_form_141", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 137, + "model": "main.medium", + "fields": { + "key": "built_form_142", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 138, + "model": "main.medium", + "fields": { + "key": "built_form_143", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 139, + "model": "main.medium", + "fields": { + "key": "built_form_144", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 140, + "model": "main.medium", + "fields": { + "key": "built_form_145", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 141, + "model": "main.medium", + "fields": { + "key": "built_form_146", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 143, + "model": "main.medium", + "fields": { + "key": "built_form_148", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 146, + "model": "main.medium", + "fields": { + "key": "built_form_152", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 147, + "model": "main.medium", + "fields": { + "key": "built_form_153", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 148, + "model": "main.medium", + "fields": { + "key": "built_form_154", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 149, + "model": "main.medium", + "fields": { + "key": "built_form_155", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 150, + "model": "main.medium", + "fields": { + "key": "built_form_156", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 151, + "model": "main.medium", + "fields": { + "key": "built_form_157", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 152, + "model": "main.medium", + "fields": { + "key": "built_form_158", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 153, + "model": "main.medium", + "fields": { + "key": "built_form_159", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 154, + "model": "main.medium", + "fields": { + "key": "built_form_160", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 155, + "model": "main.medium", + "fields": { + "key": "built_form_161", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 156, + "model": "main.medium", + "fields": { + "key": "built_form_162", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 157, + "model": "main.medium", + "fields": { + "key": "built_form_163", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 158, + "model": "main.medium", + "fields": { + "key": "built_form_165", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 159, + "model": "main.medium", + "fields": { + "key": "built_form_166", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 160, + "model": "main.medium", + "fields": { + "key": "built_form_167", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 161, + "model": "main.medium", + "fields": { + "key": "built_form_168", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 162, + "model": "main.medium", + "fields": { + "key": "built_form_169", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 163, + "model": "main.medium", + "fields": { + "key": "built_form_170", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 164, + "model": "main.medium", + "fields": { + "key": "built_form_171", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 165, + "model": "main.medium", + "fields": { + "key": "built_form_172", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 166, + "model": "main.medium", + "fields": { + "key": "built_form_173", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 167, + "model": "main.medium", + "fields": { + "key": "built_form_174", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 168, + "model": "main.medium", + "fields": { + "key": "built_form_175", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 169, + "model": "main.medium", + "fields": { + "key": "built_form_176", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 170, + "model": "main.medium", + "fields": { + "key": "built_form_177", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 171, + "model": "main.medium", + "fields": { + "key": "built_form_178", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 172, + "model": "main.medium", + "fields": { + "key": "built_form_179", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 173, + "model": "main.medium", + "fields": { + "key": "built_form_180", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 174, + "model": "main.medium", + "fields": { + "key": "built_form_181", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 175, + "model": "main.medium", + "fields": { + "key": "built_form_182", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 176, + "model": "main.medium", + "fields": { + "key": "built_form_183", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 177, + "model": "main.medium", + "fields": { + "key": "built_form_184", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 178, + "model": "main.medium", + "fields": { + "key": "built_form_185", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 179, + "model": "main.medium", + "fields": { + "key": "built_form_186", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 180, + "model": "main.medium", + "fields": { + "key": "built_form_187", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 181, + "model": "main.medium", + "fields": { + "key": "built_form_188", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 182, + "model": "main.medium", + "fields": { + "key": "built_form_189", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 183, + "model": "main.medium", + "fields": { + "key": "built_form_190", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 185, + "model": "main.medium", + "fields": { + "key": "built_form_193", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 193, + "model": "main.medium", + "fields": { + "key": "built_form_201", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 197, + "model": "main.medium", + "fields": { + "key": "built_form_205", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 198, + "model": "main.medium", + "fields": { + "key": "built_form_206", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 199, + "model": "main.medium", + "fields": { + "key": "built_form_208", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 200, + "model": "main.medium", + "fields": { + "key": "built_form_209", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 201, + "model": "main.medium", + "fields": { + "key": "built_form_210", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 202, + "model": "main.medium", + "fields": { + "key": "built_form_211", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 203, + "model": "main.medium", + "fields": { + "key": "built_form_212", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 204, + "model": "main.medium", + "fields": { + "key": "built_form_213", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 205, + "model": "main.medium", + "fields": { + "key": "built_form_214", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 206, + "model": "main.medium", + "fields": { + "key": "built_form_215", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 207, + "model": "main.medium", + "fields": { + "key": "built_form_216", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 208, + "model": "main.medium", + "fields": { + "key": "built_form_218", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 209, + "model": "main.medium", + "fields": { + "key": "built_form_219", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 210, + "model": "main.medium", + "fields": { + "key": "built_form_220", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 211, + "model": "main.medium", + "fields": { + "key": "built_form_221", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 214, + "model": "main.medium", + "fields": { + "key": "built_form_224", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 215, + "model": "main.medium", + "fields": { + "key": "built_form_225", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 216, + "model": "main.medium", + "fields": { + "key": "built_form_226", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 217, + "model": "main.medium", + "fields": { + "key": "built_form_227", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 219, + "model": "main.medium", + "fields": { + "key": "built_form_229", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 221, + "model": "main.medium", + "fields": { + "key": "built_form_231", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 222, + "model": "main.medium", + "fields": { + "key": "built_form_232", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 223, + "model": "main.medium", + "fields": { + "key": "built_form_233", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 226, + "model": "main.medium", + "fields": { + "key": "built_form_237", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 227, + "model": "main.medium", + "fields": { + "key": "built_form_238", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 228, + "model": "main.medium", + "fields": { + "key": "built_form_239", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 229, + "model": "main.medium", + "fields": { + "key": "built_form_240", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 232, + "model": "main.medium", + "fields": { + "key": "built_form_243", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 234, + "model": "main.medium", + "fields": { + "key": "built_form_245", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 235, + "model": "main.medium", + "fields": { + "key": "built_form_246", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 239, + "model": "main.medium", + "fields": { + "key": "built_form_250", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 240, + "model": "main.medium", + "fields": { + "key": "built_form_251", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 242, + "model": "main.medium", + "fields": { + "key": "built_form_254", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 281, + "model": "main.medium", + "fields": { + "key": "built_form_258", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 282, + "model": "main.medium", + "fields": { + "key": "built_form_260", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 286, + "model": "main.medium", + "fields": { + "key": "built_form_264", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 287, + "model": "main.medium", + "fields": { + "key": "built_form_265", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 288, + "model": "main.medium", + "fields": { + "key": "built_form_266", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 289, + "model": "main.medium", + "fields": { + "key": "built_form_267", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 290, + "model": "main.medium", + "fields": { + "key": "built_form_268", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 291, + "model": "main.medium", + "fields": { + "key": "built_form_269", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 292, + "model": "main.medium", + "fields": { + "key": "built_form_270", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 295, + "model": "main.medium", + "fields": { + "key": "built_form_273", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 296, + "model": "main.medium", + "fields": { + "key": "built_form_274", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 299, + "model": "main.medium", + "fields": { + "key": "built_form_277", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 302, + "model": "main.medium", + "fields": { + "key": "built_form_281", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 303, + "model": "main.medium", + "fields": { + "key": "built_form_282", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 304, + "model": "main.medium", + "fields": { + "key": "built_form_283", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 305, + "model": "main.medium", + "fields": { + "key": "built_form_284", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 309, + "model": "main.medium", + "fields": { + "key": "built_form_288", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 311, + "model": "main.medium", + "fields": { + "key": "built_form_290", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 315, + "model": "main.medium", + "fields": { + "key": "built_form_294", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 316, + "model": "main.medium", + "fields": { + "key": "built_form_295", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 317, + "model": "main.medium", + "fields": { + "key": "built_form_296", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 318, + "model": "main.medium", + "fields": { + "key": "built_form_297", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 319, + "model": "main.medium", + "fields": { + "key": "built_form_298", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 320, + "model": "main.medium", + "fields": { + "key": "built_form_299", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 321, + "model": "main.medium", + "fields": { + "key": "built_form_300", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 323, + "model": "main.medium", + "fields": { + "key": "built_form_302", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 324, + "model": "main.medium", + "fields": { + "key": "built_form_303", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 325, + "model": "main.medium", + "fields": { + "key": "built_form_304", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 326, + "model": "main.medium", + "fields": { + "key": "built_form_305", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 327, + "model": "main.medium", + "fields": { + "key": "built_form_306", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 328, + "model": "main.medium", + "fields": { + "key": "built_form_307", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 329, + "model": "main.medium", + "fields": { + "key": "built_form_309", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 331, + "model": "main.medium", + "fields": { + "key": "built_form_311", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 332, + "model": "main.medium", + "fields": { + "key": "built_form_312", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 333, + "model": "main.medium", + "fields": { + "key": "built_form_313", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 335, + "model": "main.medium", + "fields": { + "key": "built_form_315", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 336, + "model": "main.medium", + "fields": { + "key": "built_form_316", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 338, + "model": "main.medium", + "fields": { + "key": "built_form_318", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 343, + "model": "main.medium", + "fields": { + "key": "built_form_324", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 347, + "model": "main.medium", + "fields": { + "key": "built_form_328", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 348, + "model": "main.medium", + "fields": { + "key": "built_form_329", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 349, + "model": "main.medium", + "fields": { + "key": "built_form_330", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 350, + "model": "main.medium", + "fields": { + "key": "built_form_331", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 351, + "model": "main.medium", + "fields": { + "key": "built_form_332", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 352, + "model": "main.medium", + "fields": { + "key": "built_form_333", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 353, + "model": "main.medium", + "fields": { + "key": "built_form_334", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 354, + "model": "main.medium", + "fields": { + "key": "built_form_335", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 355, + "model": "main.medium", + "fields": { + "key": "built_form_336", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 356, + "model": "main.medium", + "fields": { + "key": "built_form_337", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 357, + "model": "main.medium", + "fields": { + "key": "built_form_338", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 358, + "model": "main.medium", + "fields": { + "key": "built_form_339", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 359, + "model": "main.medium", + "fields": { + "key": "built_form_340", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 360, + "model": "main.medium", + "fields": { + "key": "built_form_341", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 383, + "model": "main.medium", + "fields": { + "key": "built_form_4", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 384, + "model": "main.medium", + "fields": { + "key": "built_form_11", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 386, + "model": "main.medium", + "fields": { + "key": "built_form_48", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 387, + "model": "main.medium", + "fields": { + "key": "built_form_65", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 388, + "model": "main.medium", + "fields": { + "key": "built_form_86", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 389, + "model": "main.medium", + "fields": { + "key": "built_form_98", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 390, + "model": "main.medium", + "fields": { + "key": "built_form_114", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 392, + "model": "main.medium", + "fields": { + "key": "built_form_129", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 393, + "model": "main.medium", + "fields": { + "key": "built_form_151", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 394, + "model": "main.medium", + "fields": { + "key": "built_form_164", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 395, + "model": "main.medium", + "fields": { + "key": "built_form_191", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 396, + "model": "main.medium", + "fields": { + "key": "built_form_207", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 397, + "model": "main.medium", + "fields": { + "key": "built_form_217", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 399, + "model": "main.medium", + "fields": { + "key": "built_form_252", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 400, + "model": "main.medium", + "fields": { + "key": "built_form_259", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 246, + "model": "main.medium", + "fields": { + "key": "built_form_343", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGNTI1MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 247, + "model": "main.medium", + "fields": { + "key": "built_form_344", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzY2RkZGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 248, + "model": "main.medium", + "fields": { + "key": "built_form_345", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 249, + "model": "main.medium", + "fields": { + "key": "built_form_346", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzM2NUY5MXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 250, + "model": "main.medium", + "fields": { + "key": "built_form_347", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 251, + "model": "main.medium", + "fields": { + "key": "built_form_348", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 252, + "model": "main.medium", + "fields": { + "key": "built_form_349", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzAwOTk5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 253, + "model": "main.medium", + "fields": { + "key": "built_form_350", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 254, + "model": "main.medium", + "fields": { + "key": "built_form_351", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzVEMjY4MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 255, + "model": "main.medium", + "fields": { + "key": "built_form_352", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 256, + "model": "main.medium", + "fields": { + "key": "built_form_353", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZDRkU2RnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 257, + "model": "main.medium", + "fields": { + "key": "built_form_354", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZDRkU2RnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 258, + "model": "main.medium", + "fields": { + "key": "built_form_355", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NDNTUwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 259, + "model": "main.medium", + "fields": { + "key": "built_form_356", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 260, + "model": "main.medium", + "fields": { + "key": "built_form_357", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzAwRkZGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 261, + "model": "main.medium", + "fields": { + "key": "built_form_358", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzVEMjY4MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 262, + "model": "main.medium", + "fields": { + "key": "built_form_359", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGNjY5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 263, + "model": "main.medium", + "fields": { + "key": "built_form_360", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2MTQxNHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 264, + "model": "main.medium", + "fields": { + "key": "built_form_361", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzg3NjRBNnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 265, + "model": "main.medium", + "fields": { + "key": "built_form_362", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzVEMjY4MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 266, + "model": "main.medium", + "fields": { + "key": "built_form_363", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzg2MkM5MHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 267, + "model": "main.medium", + "fields": { + "key": "built_form_364", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 268, + "model": "main.medium", + "fields": { + "key": "built_form_366", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOTAxQXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 269, + "model": "main.medium", + "fields": { + "key": "built_form_367", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 270, + "model": "main.medium", + "fields": { + "key": "built_form_368", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 271, + "model": "main.medium", + "fields": { + "key": "built_form_369", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0RDQzE1NnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 272, + "model": "main.medium", + "fields": { + "key": "built_form_370", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk0OEE1NHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 273, + "model": "main.medium", + "fields": { + "key": "built_form_371", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOTAxQXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 274, + "model": "main.medium", + "fields": { + "key": "built_form_372", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 275, + "model": "main.medium", + "fields": { + "key": "built_form_373", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZFQ0Y1OHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 276, + "model": "main.medium", + "fields": { + "key": "built_form_374", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 277, + "model": "main.medium", + "fields": { + "key": "built_form_375", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 278, + "model": "main.medium", + "fields": { + "key": "built_form_376", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZDRkU2RnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 279, + "model": "main.medium", + "fields": { + "key": "built_form_377", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0VFRTVGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 280, + "model": "main.medium", + "fields": { + "key": "built_form_378", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzg3NjRBNnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 361, + "model": "main.medium", + "fields": { + "key": "built_form_342", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQjBBRHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 422, + "model": "main.medium", + "fields": { + "key": "built_form_365", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOUI5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 423, + "model": "main.medium", + "fields": { + "key": "built_form_379", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQTczM3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 424, + "model": "main.medium", + "fields": { + "key": "built_form_380", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NDNTUwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 425, + "model": "main.medium", + "fields": { + "key": "built_form_381", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOTAxQXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 426, + "model": "main.medium", + "fields": { + "key": "built_form_382", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 427, + "model": "main.medium", + "fields": { + "key": "built_form_383", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 428, + "model": "main.medium", + "fields": { + "key": "built_form_384", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk0MDAwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 429, + "model": "main.medium", + "fields": { + "key": "built_form_385", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 430, + "model": "main.medium", + "fields": { + "key": "built_form_386", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 431, + "model": "main.medium", + "fields": { + "key": "built_form_387", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 432, + "model": "main.medium", + "fields": { + "key": "built_form_388", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQTczM3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 433, + "model": "main.medium", + "fields": { + "key": "built_form_389", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0JDRDM5N3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 434, + "model": "main.medium", + "fields": { + "key": "built_form_390", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0E5ODFENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 435, + "model": "main.medium", + "fields": { + "key": "built_form_391", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0Y2NzlBQnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 436, + "model": "main.medium", + "fields": { + "key": "built_form_392", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGNTI1MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 437, + "model": "main.medium", + "fields": { + "key": "built_form_393", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 438, + "model": "main.medium", + "fields": { + "key": "built_form_394", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzEyMEE4RnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 439, + "model": "main.medium", + "fields": { + "key": "built_form_395", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzM2NUY5MXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 440, + "model": "main.medium", + "fields": { + "key": "built_form_396", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0RDNDFCOHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 441, + "model": "main.medium", + "fields": { + "key": "built_form_397", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0YxQzNDM3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 442, + "model": "main.medium", + "fields": { + "key": "built_form_398", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 443, + "model": "main.medium", + "fields": { + "key": "built_form_399", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 444, + "model": "main.medium", + "fields": { + "key": "built_form_400", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 5, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Office", + "description": null + } + }, + { + "pk": 8, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Education Services", + "description": null + } + }, + { + "pk": 10, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Other Services", + "description": null + } + }, + { + "pk": 3, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Retail", + "description": null + } + }, + { + "pk": 11, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Single Family Large Lot", + "description": null + } + }, + { + "pk": 2, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Residential", + "description": null + } + }, + { + "pk": 12, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Attached Single Family", + "description": null + } + }, + { + "pk": 15, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Office Services", + "description": null + } + }, + { + "pk": 7, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Restaurant", + "description": null + } + }, + { + "pk": 14, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Multifamily 5 Plus", + "description": null + } + }, + { + "pk": 13, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Arts Entertainment", + "description": null + } + }, + { + "pk": 9, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Retail Services", + "description": null + } + }, + { + "pk": 16, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Medical Services", + "description": null + } + }, + { + "pk": 17, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Accommodation", + "description": null + } + }, + { + "pk": 18, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Manufacturing", + "description": null + } + }, + { + "pk": 4, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Industrial", + "description": null + } + }, + { + "pk": 20, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Transport Warehouse", + "description": null + } + }, + { + "pk": 19, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Wholesale", + "description": null + } + }, + { + "pk": 21, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Multifamily 2 To 4", + "description": null + } + }, + { + "pk": 22, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Agriculture", + "description": null + } + }, + { + "pk": 6, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Agricultural", + "description": null + } + }, + { + "pk": 23, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Extraction", + "description": null + } + }, + { + "pk": 26, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Single Family Small Lot", + "description": null + } + }, + { + "pk": 25, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Public Admin", + "description": null + } + }, + { + "pk": 6, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 10, + "name": "Campus/College Low", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 7, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 11, + "name": "Church 1", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 8, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 12, + "name": "Church 2", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 12, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 15, + "name": "SCAG Large Lot, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 13, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 16, + "name": "Average, Beverly Hills", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 14, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 17, + "name": "Average, Old Palo Alto", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 16, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 19, + "name": "Windemere Estate, San Ramon", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 17, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 20, + "name": "Corte Bella, Irvine CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 18, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 21, + "name": "Victoria Townhomes, Seattle WA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 19, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 22, + "name": "F1 Affordable Townhomes", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 20, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 23, + "name": "Presidential Towers, Chicago", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 23, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 26, + "name": "Visionaire, New York", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 24, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 27, + "name": "The Infinity, San Francisco", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 25, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 28, + "name": "201 Folsom, San Francisco", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 26, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 29, + "name": "Atwater Place, Portland", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 27, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 30, + "name": "601 4th Ave, Seattle", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 28, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 31, + "name": "555 Mission Street, San Francisco", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 29, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 32, + "name": "55 Second Street, San Francisco", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 30, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 33, + "name": "SACOG 46. CBD Ofice", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 33, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 35, + "name": "Tabor Center, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 35, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 37, + "name": "199 New Montgomery, SF", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 36, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 38, + "name": "Pacifica Honolulu, Oahu", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 39, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 41, + "name": "AC Mid-Rise Res, 27+5 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 40, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 42, + "name": "Viridian, Nashville TN", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 41, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 43, + "name": "The Metropolitan, SF", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 42, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 44, + "name": "Pine & Franklin, SF", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 43, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 45, + "name": "Children's Hospital, Los Angeles", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 50, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 51, + "name": "Holiday Inn, Woodland Hills", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 53, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 54, + "name": "Lyons Magnus Plant #1, Fresno", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 55, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 56, + "name": "SF Produce Markets, San Francisco", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 56, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 57, + "name": "Odwalla Distribution Center, Berkeley", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 57, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 58, + "name": "FedEx Building,Gateway Office Park, South SF", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 58, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 59, + "name": "Harte-Hanks Building, Valencia Commerce Center", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 59, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 60, + "name": "SEGA Heavy Ind, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 60, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 61, + "name": "SACOG 14. Heavy Indus, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 61, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 62, + "name": "Tank Farm Light Industrial, San Luis Obispo", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 62, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 63, + "name": "SCAG Heavy Indus, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 63, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 64, + "name": "Pacific Business Center, Fremont CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 64, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 65, + "name": "SACOG B. SF Large Lot, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 66, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 66, + "name": "Estate Home, Stapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 67, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 67, + "name": "Large Lot (Average, Gold Coast, Alameda, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 69, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 69, + "name": "Guernville Rd McDonald's, Santa Rosa CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 70, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 70, + "name": "Stanford Ranch, Roseville", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 71, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 71, + "name": "Gilroy Crossing, Gilroy", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 72, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 72, + "name": "Paso Robles Strip Retail, Paso Robles", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 73, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 73, + "name": "Renaissance Center West, Las Vegas", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 74, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 74, + "name": "Mission Viejo Commerce Center", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 75, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 75, + "name": "200 Second Street, Oakland", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 76, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 76, + "name": "Cabrini First Hill Apts, Seattle", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 77, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 77, + "name": "Touriel Bldg, Berkeley", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 79, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 79, + "name": "Stone Way Apts, Seattle", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 80, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 80, + "name": "Kinsey Flats, Cincinnati, OH", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 81, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 81, + "name": "Shattuck Lofts, Berkeley", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 83, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 83, + "name": "Museum Place, Portland OR", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 84, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 84, + "name": "Gaia Bldg, Berkeley", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 85, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 85, + "name": "Fine Arts, Berkeley", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 87, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 86, + "name": "Site 17, Seattle", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 88, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 87, + "name": "Alcyone, Seattle", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 89, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 88, + "name": "1885 University/New Californian, Berkeley", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 90, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 89, + "name": "AC Low Rise Office (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 91, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 90, + "name": "CalPERS Headquarters, Sacramento", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 92, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 91, + "name": "The Terry Thomas, Seattle", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 93, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 92, + "name": "223 Yale @ Alley24, Seattle", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 94, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 93, + "name": "Symantec Headquarters, Culver City", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 95, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 94, + "name": "R.D. Merrill Building, Seattle", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 96, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 95, + "name": "SEGA Low Rise Office, 4-6 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 97, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 96, + "name": "SACOG 98. Mod Inten. Office (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 99, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 97, + "name": "3170 College Avenue, Berkeley, MU Noah's Bagels", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 100, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 98, + "name": "1853 Solano Avenue, Berkeley) (Zachary's Pizza", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 101, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 99, + "name": "1616 N Main Street, Walnut Creek) (MU Crepe Vine", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 102, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 100, + "name": "960 Cole Street, San Francisco) (Alpha Market", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 103, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 101, + "name": "1601 N Main Street, Walnut Creek) (MU Instrument Sales", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 104, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 102, + "name": "Mechanics Bank, Kensington CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 105, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 103, + "name": "Belmont Dairy, Portland OR", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 106, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 104, + "name": "International Place, Harrisburg, PN", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 111, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 109, + "name": "3400 Cesar Chavez St, SF, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 112, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 110, + "name": "Venice Renaissance, Venice CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 117, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 114, + "name": "480 Castro Street, San Francisco, MU Walgreens", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 118, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 115, + "name": "2100 Vine Street, Berkeley, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 119, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 116, + "name": "5488 College Avenue, Oakland, MU Retail", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 120, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 117, + "name": "Tanner Market, Pasadena", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 121, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 118, + "name": "Plaza Cienega, Los Angeles", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 122, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 119, + "name": "SCAG Strip Commerical, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 123, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 120, + "name": "Cap Metro Strip Commerical, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 124, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 121, + "name": "SACOG 10. Comm/Nhood Retail, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 125, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 122, + "name": "Greenway Plaza, Yonkers NY", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 131, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 126, + "name": "Kentlands, Stapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 132, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 127, + "name": "Daybreak 5500", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 134, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 129, + "name": "937 Glisan, Portland", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 135, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 130, + "name": "The Gregory Lofts, Portland", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 136, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 131, + "name": "The Edge, Portland", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 138, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 133, + "name": "Langstaff W-05", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 139, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 134, + "name": "Langstaff W-04", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 142, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 137, + "name": "EPA Headquarters (Region 8), Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 143, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 138, + "name": "SACOG 8. Hi Intensity Office", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 144, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 139, + "name": "Non-Urban Elementary School", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 145, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 140, + "name": "Non-Urban High School", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 146, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 141, + "name": "Non-Urban Middle School", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 148, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 143, + "name": "Bishop Ranch BR-3, San Ramon", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 152, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 146, + "name": "SACOG 98. Mod Inten. Office", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 153, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 147, + "name": "SEGA Low Rise Office, 4-6 floors (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 154, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 148, + "name": "Office Park High (Bishop Ranch BR-6, San Ramon", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 155, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 149, + "name": "Nanometrics Bldg, Milpitas", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 156, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 150, + "name": "Bestronics Bldg, San Jose", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 157, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 151, + "name": "Redwood Business Park, Petaluma", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 158, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 152, + "name": "Sonoma Technology Bldg, Petaluma", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 159, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 153, + "name": "Parking Structure 1", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 160, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 154, + "name": "Parking Structure 2", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 161, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 155, + "name": "Oak & Central, Alameda", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 162, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 156, + "name": "Jack London Market, Oakland", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 163, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 157, + "name": "Parking Structure 3", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 166, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 159, + "name": "Parking Structure/Mixed Use 3", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 167, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 160, + "name": "15th and Pearl Structure, Boulder, CO)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 168, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 161, + "name": "8th and Hope, Los Angeles CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 169, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 162, + "name": "Parking Structure+Ground-Floor Retail 3", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 170, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 163, + "name": "SCAG Regional Mall, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 171, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 164, + "name": "SEGA General Commerical, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 172, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 165, + "name": "Montclair Plaza, San Bernardino", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 173, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 166, + "name": "Westfield Galleria, Roseville", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 174, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 167, + "name": "Westfield Mission Valley, San Diego", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 175, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 168, + "name": "SACOG 11. Regional Retail, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 176, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 169, + "name": "Liberty Quarry Proposal, Temecula, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 177, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 170, + "name": "Mid-sized Farm near Manteca", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 178, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 171, + "name": "Medium Orchard Near Tracy", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 179, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 172, + "name": "Small Orchard Near Ojai", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 180, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 173, + "name": "Frog Hollow, Brentwood", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 181, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 174, + "name": "Live Power Farm, Covelo", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 182, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 175, + "name": "Gospel Flat Farm, Bollinas", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 183, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 176, + "name": "Chilleno Valley Ranch, Petaluma", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 184, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 177, + "name": "I-5 CA-145 Interchange", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 185, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 178, + "name": "Small Farm Near Modesto", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 186, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 179, + "name": "Large Orchard Near Tracy", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 187, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 180, + "name": "Near Modesto", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 188, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 181, + "name": "Large Farm Near Watsonville", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 190, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 183, + "name": "Quintessa Vineyard", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 193, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 185, + "name": "Castle & Cook Resorts Wind Farm Proposal, Lanai, HI", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 201, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 193, + "name": "Occidential Elk Hills Oil Field", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 205, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 197, + "name": "near Fresno", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 208, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 199, + "name": "AFT 10 acre lot", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 209, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 200, + "name": "near Chowchilla", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 210, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 201, + "name": "AFT 20 acre lot", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 211, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 202, + "name": "near Boonville", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 212, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 203, + "name": "near Fresno (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 213, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 204, + "name": "Near Fresno (3)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 214, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 205, + "name": "Ranchette 2 ", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 215, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 206, + "name": "SACOG 1. Rural Res, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 216, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 207, + "name": "SCAG Rural, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 218, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 208, + "name": "SEGA Rural, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 219, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 209, + "name": "John Hancock Center, Chicago", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 220, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 210, + "name": "Encinal Tower (Oakland, CA)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 221, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 211, + "name": "181 Fremont Street", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 224, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 214, + "name": "US Bank Tower, Los Angeles", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 226, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 216, + "name": "Aon Center, Los Angeles, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 227, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 217, + "name": "Bank of America Center, Los Angeles, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 229, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 219, + "name": "Transbay Tower", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 231, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 221, + "name": "Rincon One, San Francisco", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 232, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 222, + "name": "Langstaff W-12", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 233, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 223, + "name": "Eureka Tower, Melbourne AU", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 237, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 226, + "name": "John Laing SF, Stapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 238, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 227, + "name": "Town Square, Sapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 239, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 228, + "name": "Average, Albany", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 240, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 229, + "name": "Alameda SF Detached, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 245, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 234, + "name": "Ironhorse Family Apartments, Oakland (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 246, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 235, + "name": "MODA Lofts, Stapleton, Denver (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 250, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 239, + "name": "Alameda Small Townhouse (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 251, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 240, + "name": "SEGA Townhouse (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 254, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 242, + "name": "Backyard Row Home, Stapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 260, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 282, + "name": "Police Station, Community Services, Walnut, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 264, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 286, + "name": "Lenzen Square, San Jose", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 265, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 287, + "name": "Linden Court, Oakland", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 266, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 288, + "name": "Sonoma Villero, Bothell, WA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 267, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 289, + "name": "Town Lofts, Stapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 268, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 290, + "name": "Mabuhay Court, San Jose", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 269, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 291, + "name": "City Hall, Police Dept., St. Helena, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 270, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 292, + "name": "City Hall, Police and Fire Dept., Bishop, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 273, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 295, + "name": "St Helena Public Library, St. Helena, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 274, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 296, + "name": "Bishop Branch Library, Bishop, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 277, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 299, + "name": "Oakland City Hall, Oakland, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 282, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 303, + "name": "Long Beach Convention and Entertainment Center, Long Beach, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 283, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 304, + "name": "Rene C. Davidson Courthouse, Oakland, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 284, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 305, + "name": "Long Beach Superior Court, Long Beach, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 288, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 309, + "name": "Horace Mann ES, San Jose", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 290, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 311, + "name": "Berkeley High School, Berkeley CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 294, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 315, + "name": "Central Los Angeles Middle School", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 296, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 317, + "name": "Cubix Yerba Buena, SF", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 297, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 318, + "name": "Langstaff E-09", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 298, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 319, + "name": "AC Res Type 2/MU", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 299, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 320, + "name": "CapMetro Apt/Condo Hi", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 300, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 321, + "name": "AC Low Rise Res/MU", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 302, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 323, + "name": "SACOG 45. Intense Urban Res", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 303, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 324, + "name": "SCAG Apt/Condo Hi", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 304, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 325, + "name": "Ironhorse Family Apartments, Oakland", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 305, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 326, + "name": "MODA Lofts, Stapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 306, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 327, + "name": "Darling Florist Bldg, Berkeley", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 307, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 328, + "name": "25-35 Dolores, SF (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 309, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 329, + "name": "Oakland Public Library, Oakland, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 311, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 331, + "name": "Alameda Small Townhouse", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 313, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 333, + "name": "Penthouse Row Homes, Stapleton, Denver (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 315, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 335, + "name": "Pearl Townhome, Portland", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 316, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 336, + "name": "Sky Terrace, Stapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 318, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 338, + "name": "Denver Brownstone, Stapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 324, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 343, + "name": "Garden Courts, Stapleton, Denver", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 328, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 347, + "name": "WorkSpace - 2150 Folsom, San Francisco, CA 94110", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 329, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 348, + "name": "Dynagraphics - 300 NW 14th Avenue, Portland, OR 97209", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 330, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 349, + "name": "120 11th Street, San Francisco, CA 94103", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 331, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 350, + "name": "525 SE Pine St, Portland, OR 97214", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 333, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 352, + "name": "2181 NW Nicolai, Portland, OR 97210", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 334, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 353, + "name": "NW Trunk & Bag Building - 522 N Thompson, Portland, OR 97227", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 335, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 354, + "name": "McClaskey Building - 2755 NW 31st Avenue, Portland, OR 97210", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 336, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 355, + "name": "1360 Egbert, San Francisco, CA 94124", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 337, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 356, + "name": "111 SE Madison Ave, Portland, OR 97214", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 338, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 357, + "name": "Warehouse 3 - Proposed Emeryville IKEA (in 1.2/1.6 FAR district)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 339, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 358, + "name": "2003 West Avenue 140th, San Leandro, CA 94577 (Loopnet.com)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 340, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 359, + "name": "9040 Carroll Way, San Diego, CA 92121 (Propertyline.com)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 341, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 360, + "name": "2300 Cesar Chavez, San Francisco, CA 94124", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 4, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 383, + "name": "LA City College, Los Angeles", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 11, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 384, + "name": "SACOG 2. Very Low Den Res, 1-2 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 48, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 386, + "name": "Holiday Inn Express, Truckee", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 65, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 387, + "name": "Average, View Park, Los Angeles", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 86, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 388, + "name": "East End Gateway, Sacramento", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 114, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 390, + "name": "300 30th Street at Church, San Francisco, MU Church Produce", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 129, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 392, + "name": "Average, St. Francis Wood, San Francisco", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 151, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 393, + "name": "SEGA Office Park 0.35, 2-4 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 164, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 394, + "name": "Fahrenheit Condos + Petco Parkade, San Diego CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 191, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 395, + "name": "Napa Valley Wine Company", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 207, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 396, + "name": "AFT 1.5 acre lot", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 217, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 397, + "name": "Prairie Crossing Rural SF, Grayslake", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 259, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 400, + "name": "City Hall, Library, Gym and Teen Center, Walnut, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 15, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 18, + "name": "Daybreak Estate, South Jordan", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 34, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 36, + "name": "560 Mission Street, San Francisco", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 45, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 47, + "name": "Four Seasons, San Francisco", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 46, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 48, + "name": "Sheraton Grand, Sacramento", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 47, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 49, + "name": "Walt Disney World Dolphin, Orlando", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 49, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 50, + "name": "La Quinta Inn, Redding", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 141, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 136, + "name": "SCAG City Center Office, 6-15 floors", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 165, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 158, + "name": "Parking Structure/Mixed Use 2", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 189, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 182, + "name": "Martin Stelling Vineyard", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 206, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 198, + "name": "AFT 5 acre lot", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 225, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 215, + "name": "Washington Mutual Tower, Seattle", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 243, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 232, + "name": "Avalon Apts (Cahill Park), San Jose", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 258, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 281, + "name": "City Hall, Library, Rec Center, Menlo Park, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 281, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 302, + "name": "Oakland Convention Center, Oakland, CA", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 295, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 316, + "name": "Eddy + Taylor Family Housing, SF", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 312, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 332, + "name": "Backyard Row Home, Stapleton, Denver (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 332, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 351, + "name": "1154-1158 Howard Street, San Francisco, CA 94103", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 98, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 389, + "name": "4185 Piedmont Avenue, Oakland) (Dentist Office", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 252, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 399, + "name": "Sky Terrace, Stapleton, Denver (2)", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 344, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 247, + "name": "High-Rise Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 345, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 248, + "name": "Non-Urban Middle School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 346, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 249, + "name": "Campus/College Low", + "tags": [ + 3 + ], + "description": null + } + }, + { + "pk": 347, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 250, + "name": "Industrial Low", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 348, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 251, + "name": "Non-Urban High School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 349, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 252, + "name": "Urban Mid-Rise Residential", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 350, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 253, + "name": "Urban Middle School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 351, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 254, + "name": "High-Rise Office", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 352, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 255, + "name": "Estate Lot", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 353, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 256, + "name": "Medium Lot 5500", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 354, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 257, + "name": "Small Lot 4000", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 355, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 258, + "name": "Skyscraper Residential", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 356, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 259, + "name": "Urban High School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 357, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 260, + "name": "Mid-Rise Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 358, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 261, + "name": "Hotel High", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 359, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 262, + "name": "Parking Structure/Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 360, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 263, + "name": "Low-Rise Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 361, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 264, + "name": "Low-Rise Office", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 362, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 265, + "name": "Skyscraper Office", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 363, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 266, + "name": "Mid-Rise Office", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 364, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 267, + "name": "Warehouse High", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 367, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 269, + "name": "Town/Branch Library", + "tags": [ + 7 + ], + "description": null + } + }, + { + "pk": 368, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 270, + "name": "Suburban Civic Complex", + "tags": [ + 7 + ], + "description": null + } + }, + { + "pk": 369, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 271, + "name": "Rural Ranchette", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 370, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 272, + "name": "Parking Structure", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 371, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 273, + "name": "Urban Podium Multi-Family", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 372, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 274, + "name": "Town Civic Complex", + "tags": [ + 7 + ], + "description": null + } + }, + { + "pk": 373, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 275, + "name": "Garden Apartment", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 374, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 276, + "name": "Urban Convention Center", + "tags": [ + 7 + ], + "description": null + } + }, + { + "pk": 375, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 277, + "name": "Urban Public Library", + "tags": [ + 7 + ], + "description": null + } + }, + { + "pk": 376, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 278, + "name": "Very Small Lot 3000", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 377, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 279, + "name": "Office Park Low", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 378, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 280, + "name": "Hotel Low", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 343, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 246, + "name": "Main Street Commercial/MU Low (1-2 Floors)", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 342, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 361, + "name": "Low Intensity Strip Commercial (weighted avg)", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 379, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 423, + "name": "Urban Townhome/Live-Work", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 380, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 424, + "name": "High-Rise Residential", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 381, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 425, + "name": "Suburban Multifamily Apt/Condo", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 382, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 426, + "name": "Rural Residential", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 383, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 427, + "name": "Urban Elementary School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 384, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 428, + "name": "Skyscraper Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 385, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 429, + "name": "Industrial High", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 386, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 430, + "name": "Urban City Hall", + "tags": [ + 7 + ], + "description": null + } + }, + { + "pk": 387, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 431, + "name": "Non-Urban Elementary School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 388, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 432, + "name": "Standard Townhome", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 389, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 433, + "name": "Rural Employment", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 390, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 434, + "name": "Office Park High", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 391, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 435, + "name": "Regional Mall", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 392, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 436, + "name": "Main Street Commercial/MU High (3-5 Floors)", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 393, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 437, + "name": "Urban Courthouse", + "tags": [ + 7 + ], + "description": null + } + }, + { + "pk": 394, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 438, + "name": "Church", + "tags": [ + 7 + ], + "description": null + } + }, + { + "pk": 395, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 439, + "name": "Campus/College High", + "tags": [ + 3 + ], + "description": null + } + }, + { + "pk": 397, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 441, + "name": "Parking Structure+Ground-Floor Retail", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 398, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 442, + "name": "Warehouse Low", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 399, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 443, + "name": "Large Lot 7500", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 400, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 444, + "name": "Hospital/Civic/Other Institutional", + "tags": [ + 3 + ], + "description": null + } + }, + { + "pk": 366, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 268, + "name": "Standard Podium Multi-Family", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 365, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 422, + "name": "Medium Intensity Strip Commercial (weighted avg)", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 396, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 440, + "name": "Main Street Commercial (Retail + Office/Medical)", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 3, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 22.200, + "combined_pop_emp_density": 17.0889, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 5, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 23.700, + "combined_pop_emp_density": 29.6208, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 8, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.272, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.890, + "household_density": 1.000, + "total_far": 0.2800000, + "floors": 2.000, + "residential_average_lot_size": 10000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 9, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 8.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.141, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 0.1500000, + "floors": 2.000, + "residential_average_lot_size": 20000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 11, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 8.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.191, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.450, + "household_density": 1.000, + "total_far": 0.2500000, + "floors": 2.000, + "residential_average_lot_size": 20000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 12, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.299, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.3279257, + "floors": 2.000, + "residential_average_lot_size": 9801.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 13, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 16.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.365, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.4764468, + "floors": 2.000, + "residential_average_lot_size": 10402.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 14, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 18664.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 16, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 45.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.267, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 14850.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 3.000, + "residential_average_lot_size": 1409.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 17, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 161.200, + "combined_pop_emp_density": 7.0301, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.150, + "impervious_roof_percent": 0.150, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 53196.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.890, + "total_far": 6.6000000, + "floors": 49.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.225 + } + }, + { + "pk": 21, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 391.100, + "combined_pop_emp_density": 10.0813, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.400, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 129072.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.920, + "total_far": 15.6000000, + "floors": 41.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.300 + } + }, + { + "pk": 22, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 417.600, + "combined_pop_emp_density": 5.6609, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 137817.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.030, + "household_density": 0.940, + "total_far": 11.8000000, + "floors": 40.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.350 + } + }, + { + "pk": 23, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 294.000, + "combined_pop_emp_density": 6.5398, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 97029.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.920, + "total_far": 8.5000000, + "floors": 23.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.300 + } + }, + { + "pk": 24, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 1632.8602, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.220, + "total_far": 20.1000000, + "floors": 39.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.550 + } + }, + { + "pk": 25, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 190.600, + "combined_pop_emp_density": 2395.2082, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 62898.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 16.5000000, + "floors": 33.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 27, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 62.400, + "combined_pop_emp_density": 455.0866, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.170, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 20588.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 3.4000000, + "floors": 20.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 30, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 477.000, + "combined_pop_emp_density": 1467.4754, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 157410.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 11.0000000, + "floors": 31.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 31, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 351.700, + "combined_pop_emp_density": 2758.1186, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 116076.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 19.0000000, + "floors": 31.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 33, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 400.700, + "combined_pop_emp_density": 2.7916, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.120, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 132233.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.970, + "total_far": 5.5000000, + "floors": 46.000, + "residential_average_lot_size": 43560.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.425 + } + }, + { + "pk": 36, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 199.200, + "combined_pop_emp_density": 4.0338, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.188, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 65735.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.940, + "total_far": 6.0000000, + "floors": 32.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.350 + } + }, + { + "pk": 38, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 405.800, + "combined_pop_emp_density": 2.8491, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 133897.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.980, + "total_far": 12.8000000, + "floors": 27.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 39, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 388.900, + "combined_pop_emp_density": 2.5729, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.970, + "impervious_roof_percent": 0.970, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 128333.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.250, + "household_density": 0.990, + "total_far": 11.9000000, + "floors": 24.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.475 + } + }, + { + "pk": 40, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 176.900, + "combined_pop_emp_density": 177.7248, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 58360.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 3.0000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 42, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 248.200, + "combined_pop_emp_density": 1151.1720, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 81909.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 17.1000000, + "floors": 40.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 43, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 87.100, + "combined_pop_emp_density": 430.8480, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 28749.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 6.4000000, + "floors": 27.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 45, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.300, + "combined_pop_emp_density": 67.3200, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 46, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.300, + "combined_pop_emp_density": 13.4640, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 47, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 62.400, + "combined_pop_emp_density": 47.1240, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.7000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 50, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 6.000, + "combined_pop_emp_density": 23.1412, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 52, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 27.7695, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 53, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 2.700, + "combined_pop_emp_density": 21.0172, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 54, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 81.000, + "combined_pop_emp_density": 9.6048, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 55, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 43.500, + "combined_pop_emp_density": 14.5658, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 56, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 25.200, + "combined_pop_emp_density": 9.2565, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 57, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.000, + "combined_pop_emp_density": 8.2280, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 58, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.100, + "combined_pop_emp_density": 6.0042, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 59, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.000, + "combined_pop_emp_density": 14.8264, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 61, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.318, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.650, + "household_density": 1.000, + "total_far": 0.3733333, + "floors": 2.000, + "residential_average_lot_size": 7500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 62, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.398, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.650, + "household_density": 1.000, + "total_far": 0.5333333, + "floors": 2.000, + "residential_average_lot_size": 7500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 63, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 16.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.880, + "impervious_roof_percent": 0.880, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.700, + "household_density": 1.000, + "total_far": 0.6126634, + "floors": 3.000, + "residential_average_lot_size": 7802.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 64, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.398, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.5333333, + "floors": 2.000, + "residential_average_lot_size": 7500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 66, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 26.700, + "combined_pop_emp_density": 5.8080, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 67, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 103.100, + "combined_pop_emp_density": 9.8736, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 68, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 91.700, + "combined_pop_emp_density": 14.8104, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 69, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 95.200, + "combined_pop_emp_density": 13.4219, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 70, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 57.200, + "combined_pop_emp_density": 10.4219, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 71, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 41.800, + "combined_pop_emp_density": 11.6160, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 73, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 109.700, + "combined_pop_emp_density": 8.2635, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 36194.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.850, + "total_far": 3.5000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.125 + } + }, + { + "pk": 74, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 45.600, + "combined_pop_emp_density": 3.3041, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 15031.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.940, + "total_far": 3.4000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.350 + } + }, + { + "pk": 76, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 154.900, + "combined_pop_emp_density": 4.1208, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 51126.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.900, + "total_far": 2.4000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 78, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 240.700, + "combined_pop_emp_density": 5.3680, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 79420.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.900, + "total_far": 4.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 80, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 239.100, + "combined_pop_emp_density": 31.3312, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 78903.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.250, + "household_density": 0.700, + "total_far": 5.0000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.750 + } + }, + { + "pk": 82, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 107.500, + "combined_pop_emp_density": 4.5885, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 35484.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.900, + "total_far": 3.0000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 83, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 171.900, + "combined_pop_emp_density": 3.4326, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 56740.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.930, + "total_far": 2.9000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.325 + } + }, + { + "pk": 85, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 196.400, + "combined_pop_emp_density": 2.5070, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 64808.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.990, + "total_far": 4.1000000, + "floors": 7.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.475 + } + }, + { + "pk": 86, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 154.000, + "combined_pop_emp_density": 4.4042, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 50820.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.900, + "total_far": 3.2000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 87, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 74.100, + "combined_pop_emp_density": 275.2862, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 24437.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.400, + "household_density": 0.000, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 88, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 177.900, + "combined_pop_emp_density": 476.0486, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.300, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 58720.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 4.5000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 89, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 250.000, + "combined_pop_emp_density": 488.4281, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 82500.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.400, + "household_density": 0.000, + "total_far": 3.1000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 91, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 155.000, + "combined_pop_emp_density": 128.3568, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.260, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 51150.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 1.3000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 92, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 78.600, + "combined_pop_emp_density": 242.0019, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.934, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 25928.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 2.8000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 93, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.000, + "combined_pop_emp_density": 105.7886, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.250, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12218.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 94, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.800, + "combined_pop_emp_density": 123.4200, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.250, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12463.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 95, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 78.000, + "combined_pop_emp_density": 133.9989, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.400, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.9000000, + "floors": 2.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 96, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 59.000, + "combined_pop_emp_density": 56.4206, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 13895.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 97, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 29.600, + "combined_pop_emp_density": 56.4206, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.8000000, + "floors": 1.000, + "residential_average_lot_size": 7117.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 98, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.800, + "combined_pop_emp_density": 91.6834, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.3000000, + "floors": 2.000, + "residential_average_lot_size": 6956.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 99, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.300, + "combined_pop_emp_density": 63.4731, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.9000000, + "floors": 1.000, + "residential_average_lot_size": 3600.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 101, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 57.200, + "combined_pop_emp_density": 49.3680, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.300, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 102, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.000, + "combined_pop_emp_density": 11.5358, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 16400.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.780, + "total_far": 2.2000000, + "floors": 5.000, + "residential_average_lot_size": 6280.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.950 + } + }, + { + "pk": 103, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 174.700, + "combined_pop_emp_density": 28.7022, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 57649.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.690, + "total_far": 2.9000000, + "floors": 3.000, + "residential_average_lot_size": 3333.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.725 + } + }, + { + "pk": 108, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 130.900, + "combined_pop_emp_density": 11.5160, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 43180.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.810, + "total_far": 2.8000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.025 + } + }, + { + "pk": 109, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 295.000, + "combined_pop_emp_density": 10.8875, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.700, + "impervious_roof_percent": 0.700, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 97350.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.770, + "total_far": 1.9000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.925 + } + }, + { + "pk": 114, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 32.1733, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.520, + "total_far": 1.9000000, + "floors": 2.000, + "residential_average_lot_size": 9152.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.300 + } + }, + { + "pk": 115, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 33.7982, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.520, + "total_far": 2.0000000, + "floors": 2.000, + "residential_average_lot_size": 8958.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.300 + } + }, + { + "pk": 117, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.200, + "combined_pop_emp_density": 44.0398, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 1995.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.9000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 118, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.600, + "combined_pop_emp_density": 19.8808, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 9339.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 119, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 55.500, + "combined_pop_emp_density": 9.2565, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 120, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 46.300, + "combined_pop_emp_density": 7.5533, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 121, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 51.800, + "combined_pop_emp_density": 14.8104, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 126, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 9655.07, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.597, + "impervious_roof_percent": 0.321, + "softscape_and_landscape_percent": 0.403, + "impervious_hardscape_percent": 0.276, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 128, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.338, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 1.000, + "total_far": 0.4363636, + "floors": 2.000, + "residential_average_lot_size": 5500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 129, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.293, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.3472727, + "floors": 2.000, + "residential_average_lot_size": 5500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 131, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 248.300, + "combined_pop_emp_density": 4.9036, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 81935.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.940, + "total_far": 9.1000000, + "floors": 16.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.350 + } + }, + { + "pk": 132, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 223.200, + "combined_pop_emp_density": 13.3097, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 73669.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.840, + "total_far": 7.4000000, + "floors": 12.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.100 + } + }, + { + "pk": 133, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 223.300, + "combined_pop_emp_density": 92.5185, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 73672.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.570, + "total_far": 7.1000000, + "floors": 11.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.425 + } + }, + { + "pk": 135, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 85.200, + "combined_pop_emp_density": 1135.4640, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.511, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 28102.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 4.6000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 136, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 85.200, + "combined_pop_emp_density": 923.1322, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.511, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 28102.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 4.6000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 138, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 55.500, + "combined_pop_emp_density": 286.9779, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.272, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 18327.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 3.0000000, + "floors": 11.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 139, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.700, + "combined_pop_emp_density": 592.4160, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.622, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 10454.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 5.6000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 141, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 2.6928, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 142, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.600, + "combined_pop_emp_density": 1.8513, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2500000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 143, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 1.9747, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 145, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 118.000, + "combined_pop_emp_density": 86.3940, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.766, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 11680.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.7000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 148, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 45.400, + "combined_pop_emp_density": 25.9182, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.358, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 4490.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 0.3500000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 150, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 129.600, + "combined_pop_emp_density": 123.4200, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.887, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12829.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 151, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 49.200, + "combined_pop_emp_density": 49.3680, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.394, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 4868.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 152, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 62.000, + "combined_pop_emp_density": 11.6508, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 153, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 43.000, + "combined_pop_emp_density": 12.4407, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 154, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 54.000, + "combined_pop_emp_density": 16.2421, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 155, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 44.800, + "combined_pop_emp_density": 16.4560, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 156, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 528.000, + "combined_pop_emp_density": 3.0492, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.875, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 174240.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 3.5000000, + "floors": 4.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 157, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 660.000, + "combined_pop_emp_density": 3.9204, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 217800.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 4.5000000, + "floors": 5.000, + "residential_average_lot_size": 7117.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 158, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 435.600, + "combined_pop_emp_density": 2.1780, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 2.5000000, + "floors": 3.000, + "residential_average_lot_size": 19000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 160, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 792.000, + "combined_pop_emp_density": 4.7916, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.917, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 261360.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 5.5000000, + "floors": 6.000, + "residential_average_lot_size": 6956.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 163, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 660.000, + "combined_pop_emp_density": 23.4656, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 217800.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.500, + "total_far": 2.4000000, + "floors": 6.000, + "residential_average_lot_size": 6956.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.250 + } + }, + { + "pk": 164, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 726.000, + "combined_pop_emp_density": 34.7961, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 239580.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 5.5000000, + "floors": 6.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 165, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 632.000, + "combined_pop_emp_density": 39.6396, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 208560.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 6.5000000, + "floors": 7.000, + "residential_average_lot_size": 7117.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 166, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 381.200, + "combined_pop_emp_density": 21.7800, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 125779.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 2.5000000, + "floors": 3.000, + "residential_average_lot_size": 6956.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 167, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 64.800, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 169, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 108.600, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 10755.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 170, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 93.800, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 9285.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 171, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 65.600, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 6490.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 172, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 62.900, + "combined_pop_emp_density": 13.4594, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 173, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 174, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.100, + "combined_pop_emp_density": 0.0335, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.003, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0020000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 175, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 177, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0293, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 178, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0542, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.020, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.050 + } + }, + { + "pk": 179, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0791, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.030, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.075 + } + }, + { + "pk": 180, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 181, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 183, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 184, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0791, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.030, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.075 + } + }, + { + "pk": 185, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0293, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 186, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0791, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.030, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.075 + } + }, + { + "pk": 188, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0293, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 190, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.400, + "combined_pop_emp_density": 0.0566, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.016, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0130000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 198, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 203, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.016, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.040, + "household_density": 1.000, + "total_far": 0.0160698, + "floors": 2.000, + "residential_average_lot_size": 217800.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 204, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.052, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.120, + "household_density": 1.000, + "total_far": 0.0535660, + "floors": 2.000, + "residential_average_lot_size": 65340.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 205, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.500, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.008, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.020, + "household_density": 1.000, + "total_far": 0.0080349, + "floors": 2.000, + "residential_average_lot_size": 435600.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 206, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.009, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.020, + "household_density": 1.000, + "total_far": 0.0097222, + "floors": 2.000, + "residential_average_lot_size": 360000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 207, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.004, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.010, + "household_density": 1.000, + "total_far": 0.0040174, + "floors": 2.000, + "residential_average_lot_size": 871200.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 209, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.003, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.010, + "household_density": 1.000, + "total_far": 0.0038462, + "floors": 2.000, + "residential_average_lot_size": 910000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 210, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.016, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.040, + "household_density": 1.000, + "total_far": 0.0166667, + "floors": 2.000, + "residential_average_lot_size": 210000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 211, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 2.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.034, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.080, + "household_density": 1.000, + "total_far": 0.0350000, + "floors": 2.000, + "residential_average_lot_size": 100000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 212, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.028, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.0312500, + "floors": 2.000, + "residential_average_lot_size": 80000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 214, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.500, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.103, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 0.1388146, + "floors": 2.000, + "residential_average_lot_size": 28801.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 215, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.031, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.0375000, + "floors": 2.000, + "residential_average_lot_size": 80000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 216, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 295.000, + "combined_pop_emp_density": 466.8331, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 97350.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.600, + "total_far": 26.8000000, + "floors": 100.000, + "residential_average_lot_size": 85.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.500 + } + }, + { + "pk": 217, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 543.000, + "combined_pop_emp_density": 491.3381, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 179190.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.400, + "household_density": 0.450, + "total_far": 18.4000000, + "floors": 56.000, + "residential_average_lot_size": 174.90, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.125 + } + }, + { + "pk": 218, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 677.000, + "combined_pop_emp_density": 3418.0967, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 223410.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.400, + "household_density": 0.330, + "total_far": 51.4000000, + "floors": 66.000, + "residential_average_lot_size": 43560.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.825 + } + }, + { + "pk": 221, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1498.400, + "combined_pop_emp_density": 2969.5970, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 494485.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 21.3000000, + "floors": 73.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 222, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1407.000, + "combined_pop_emp_density": 2788.3540, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 464306.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 20.0000000, + "floors": 55.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 223, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 952.000, + "combined_pop_emp_density": 4723.3209, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 314160.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 33.2000000, + "floors": 62.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 224, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 534.000, + "combined_pop_emp_density": 1052.7884, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.200, + "impervious_roof_percent": 0.200, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 176220.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 7.4000000, + "floors": 55.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 228, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 545.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.400, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 179852.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.030, + "household_density": 1.000, + "total_far": 18.9000000, + "floors": 60.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 229, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 672.400, + "combined_pop_emp_density": 2.9733, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.331, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 221899.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.020, + "household_density": 0.980, + "total_far": 15.9000000, + "floors": 48.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 230, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 672.500, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 201755.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 17.8000000, + "floors": 91.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 234, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.497, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.6793400, + "floors": 2.000, + "residential_average_lot_size": 4182.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 235, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.462, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.6122931, + "floors": 2.000, + "residential_average_lot_size": 4230.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 236, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.344, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.3750000, + "floors": 2.000, + "residential_average_lot_size": 4000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 240, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 74.100, + "combined_pop_emp_density": 3.2479, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 24437.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.920, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.300 + } + }, + { + "pk": 243, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 127.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.970, + "impervious_roof_percent": 0.970, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 42048.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 247, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 60.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 19800.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 1452.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 249, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 32.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.234, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 10649.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 0.7000000, + "floors": 3.000, + "residential_average_lot_size": 2519.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 251, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 44.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 14520.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 1917.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 255, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.500, + "combined_pop_emp_density": 8.2280, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 256, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 7.400, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 257, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.200, + "combined_pop_emp_density": 8.2280, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 261, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 109.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 36034.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 262, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 98.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 32353.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 263, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 48.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.650, + "impervious_roof_percent": 0.650, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 4764.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 265, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 112.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.750, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 37049.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 266, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 8.100, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 267, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.900, + "combined_pop_emp_density": 32.9120, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 271, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 23.700, + "combined_pop_emp_density": 49.3680, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 274, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 12.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 278, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 67.1405, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 279, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 19.5497, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 280, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 69.1152, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.7000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 281, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 10.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 285, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.100, + "combined_pop_emp_density": 4.4431, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 291, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.500, + "combined_pop_emp_density": 4.9368, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 292, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 150.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 49500.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 8.0000000, + "floors": 14.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 294, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 110.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.590, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 36300.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 5.9000000, + "floors": 10.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 295, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 65.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.247, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 21450.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 4.2000000, + "floors": 17.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 296, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 50.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.970, + "impervious_roof_percent": 0.970, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 16500.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 1.000, + "total_far": 3.0000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 297, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 109.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 36036.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 3.0000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 300, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 133.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.750, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 43986.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 3.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 301, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 76.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.425, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 25114.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 1.7000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 302, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 127.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.970, + "impervious_roof_percent": 0.970, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 42048.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 1.000, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 303, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 239.900, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 79176.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 4.5000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 306, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 39.4944, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 308, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 45.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 14850.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 1452.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 310, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 68.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 22681.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.450, + "household_density": 1.000, + "total_far": 1.2000000, + "floors": 3.000, + "residential_average_lot_size": 2219.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 312, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 32.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.550, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 10643.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 1.1000000, + "floors": 2.000, + "residential_average_lot_size": 1742.40, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 313, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.500, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.233, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12372.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 0.7000000, + "floors": 3.000, + "residential_average_lot_size": 2519.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 315, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 38.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.375, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12646.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 1.5000000, + "floors": 4.000, + "residential_average_lot_size": 2897.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 321, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.595, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.7090643, + "floors": 2.000, + "residential_average_lot_size": 2736.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 1, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 63.800, + "combined_pop_emp_density": 33.4900, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 21054.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 4, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.000, + "combined_pop_emp_density": 14.8104, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 10, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.255, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.3333333, + "floors": 2.000, + "residential_average_lot_size": 15000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 15, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 78.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.433, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 25971.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 1.3000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 20, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 135.500, + "combined_pop_emp_density": 3.0076, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 44715.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.980, + "total_far": 15.2000000, + "floors": 35.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 26, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 191.600, + "combined_pop_emp_density": 2147.5080, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 63214.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 14.5000000, + "floors": 26.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 32, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 275.900, + "combined_pop_emp_density": 3.6543, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 91033.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.970, + "total_far": 16.6000000, + "floors": 20.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.425 + } + }, + { + "pk": 37, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 803.600, + "combined_pop_emp_density": 2.9100, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 265188.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.980, + "total_far": 13.2000000, + "floors": 31.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 44, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 39.600, + "combined_pop_emp_density": 33.6600, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 27.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 60, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 33.800, + "combined_pop_emp_density": 37.0260, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 72, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 178.000, + "combined_pop_emp_density": 5.8091, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 58740.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.890, + "total_far": 3.8000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.225 + } + }, + { + "pk": 77, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 131.800, + "combined_pop_emp_density": 7.5872, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 43482.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.760, + "total_far": 1.2000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.900 + } + }, + { + "pk": 81, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 138.800, + "combined_pop_emp_density": 14.9156, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 45787.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.830, + "total_far": 5.7000000, + "floors": 7.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.075 + } + }, + { + "pk": 84, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 197.800, + "combined_pop_emp_density": 2.5934, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 65277.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.980, + "total_far": 4.6000000, + "floors": 7.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 90, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 252.500, + "combined_pop_emp_density": 151.9294, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 83334.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.400, + "total_far": 4.8000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.000 + } + }, + { + "pk": 100, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.100, + "combined_pop_emp_density": 77.5783, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.1000000, + "floors": 2.000, + "residential_average_lot_size": 8196.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 111, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 25.1966, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.600, + "total_far": 2.0000000, + "floors": 2.000, + "residential_average_lot_size": 2395.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.500 + } + }, + { + "pk": 116, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 35.800, + "combined_pop_emp_density": 26.8126, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.520, + "total_far": 1.6000000, + "floors": 2.000, + "residential_average_lot_size": 8559.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.300 + } + }, + { + "pk": 122, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 129.800, + "combined_pop_emp_density": 20.4442, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 12846.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 325, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 10.900, + "combined_pop_emp_density": 98.7360, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.0000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 326, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 143.1672, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.9000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 327, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 6.500, + "combined_pop_emp_density": 44.4312, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.2000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 328, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.600, + "combined_pop_emp_density": 77.7546, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.1000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 329, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.600, + "combined_pop_emp_density": 103.6728, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.8000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 330, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 28.000, + "combined_pop_emp_density": 99.9702, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.7000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 331, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 10.000, + "combined_pop_emp_density": 85.1598, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 332, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 21.000, + "combined_pop_emp_density": 29.6208, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 333, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.600, + "combined_pop_emp_density": 37.0260, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 334, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 30.000, + "combined_pop_emp_density": 85.1598, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 335, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.000, + "combined_pop_emp_density": 3.0294, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.070, + "hardscape_percent": 0.931, + "impervious_roof_percent": 0.677, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.254, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.4500000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 336, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.200, + "combined_pop_emp_density": 21.2900, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.070, + "hardscape_percent": 0.931, + "impervious_roof_percent": 0.677, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.254, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.6900000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 337, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 29.000, + "combined_pop_emp_density": 13.2676, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.877, + "impervious_roof_percent": 0.434, + "softscape_and_landscape_percent": 0.123, + "impervious_hardscape_percent": 0.443, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.4300000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 803.68, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 338, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 8.000, + "combined_pop_emp_density": 20.9814, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.070, + "hardscape_percent": 0.931, + "impervious_roof_percent": 0.677, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.254, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.6800000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 140, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.100, + "combined_pop_emp_density": 189.6719, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.170, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 10263.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 1.7000000, + "floors": 10.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 149, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 132.200, + "combined_pop_emp_density": 111.5717, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.951, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 13086.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 159, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 784.100, + "combined_pop_emp_density": 3.9204, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 4.5000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 161, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 396.000, + "combined_pop_emp_density": 8.6552, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.200, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 130680.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.500, + "total_far": 0.8000000, + "floors": 4.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.250 + } + }, + { + "pk": 162, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 528.000, + "combined_pop_emp_density": 16.0604, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.320, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 174240.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.500, + "total_far": 1.6000000, + "floors": 5.000, + "residential_average_lot_size": 7117.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.250 + } + }, + { + "pk": 168, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 64.800, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 6414.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 176, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0293, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 182, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.100, + "combined_pop_emp_density": 0.0378, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.004, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0030000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 187, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0542, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.020, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.050 + } + }, + { + "pk": 202, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 2.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.042, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.120, + "household_density": 1.000, + "total_far": 0.0437500, + "floors": 2.000, + "residential_average_lot_size": 80000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 208, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.003, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.010, + "household_density": 1.000, + "total_far": 0.0035000, + "floors": 2.000, + "residential_average_lot_size": 1000000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 213, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.015, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 0.0187500, + "floors": 2.000, + "residential_average_lot_size": 160000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 226, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 181.000, + "combined_pop_emp_density": 2355.9513, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.150, + "impervious_roof_percent": 0.150, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 59730.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.250, + "household_density": 0.000, + "total_far": 19.5000000, + "floors": 80.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 237, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.569, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.8250000, + "floors": 2.000, + "residential_average_lot_size": 4000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 242, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 79.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.425, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 26161.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 1.7000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 248, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 43.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 14374.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 2000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 264, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 77.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.844, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 7666.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 1.3000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 270, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.000, + "combined_pop_emp_density": 29.6208, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 287, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.500, + "combined_pop_emp_density": 5.9242, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 1815.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 293, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 31.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 6.7000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 299, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 150.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.450, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 49708.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 2.7000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 304, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 88.900, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.650, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 29337.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 2.6000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 309, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 48.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 15857.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 1917.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 339, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 58.165, + "combined_pop_emp_density": 8.9196, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.483, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.368, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1750000, + "floors": 1.050, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 340, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 10.740, + "combined_pop_emp_density": 28.8759, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.997, + "impervious_roof_percent": 0.997, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.536, + "total_far": 1.8500000, + "floors": 2.000, + "residential_average_lot_size": 7583.90, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.721 + } + }, + { + "pk": 342, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 1.9747, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 344, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.330, + "combined_pop_emp_density": 6.4395, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2800000, + "floors": 1.500, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 346, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 93.010, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.633, + "impervious_roof_percent": 0.633, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 27564.51, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.098, + "household_density": 1.000, + "total_far": 6.5600000, + "floors": 11.400, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 348, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 283.200, + "combined_pop_emp_density": 1748.5083, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 93457.04, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.440, + "household_density": 0.000, + "total_far": 12.5300000, + "floors": 28.700, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 351, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.660, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.448, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.570, + "household_density": 1.000, + "total_far": 0.5824899, + "floors": 2.000, + "residential_average_lot_size": 20535.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 353, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.500, + "combined_pop_emp_density": 5.9242, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 1815.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 355, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 142.040, + "combined_pop_emp_density": 639.5400, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.530, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.320, + "parking_structure_square_feet": 44263.56, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 9.5000000, + "floors": 32.200, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 357, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 186.298, + "combined_pop_emp_density": 6.9261, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.955, + "impervious_roof_percent": 0.955, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 61475.58, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.317, + "household_density": 0.870, + "total_far": 4.1180000, + "floors": 6.400, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.919 + } + }, + { + "pk": 359, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 914.480, + "combined_pop_emp_density": 2770.6575, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.520, + "impervious_roof_percent": 0.520, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 301780.38, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.000, + "total_far": 20.2800000, + "floors": 65.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 361, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.340, + "combined_pop_emp_density": 43.3250, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.5550000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 363, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 88.465, + "combined_pop_emp_density": 2.5668, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.573, + "impervious_roof_percent": 0.573, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 29184.83, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.088, + "household_density": 0.956, + "total_far": 1.9400000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.289 + } + }, + { + "pk": 365, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.180, + "combined_pop_emp_density": 11.2594, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.560, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.290, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1300000, + "floors": 2.700, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 368, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 174.725, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.489, + "impervious_roof_percent": 0.489, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 57665.86, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.143, + "household_density": 1.000, + "total_far": 3.4600000, + "floors": 4.950, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 370, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 59.170, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.210, + "impervious_roof_percent": 0.210, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 19521.48, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.182, + "household_density": 1.000, + "total_far": 0.9500000, + "floors": 3.000, + "residential_average_lot_size": 493.15, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 372, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 39.4944, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 374, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 48.550, + "combined_pop_emp_density": 15.4107, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.537, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.312, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2600000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 376, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 46.690, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.212, + "impervious_roof_percent": 0.212, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 15402.98, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.210, + "household_density": 1.000, + "total_far": 1.0750000, + "floors": 2.700, + "residential_average_lot_size": 2021.72, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 378, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 89.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.560, + "impervious_roof_percent": 0.560, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 23573.76, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.9600000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 380, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.100, + "combined_pop_emp_density": 4.4431, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 382, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.775, + "combined_pop_emp_density": 21.8223, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5100000, + "floors": 1.550, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 384, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 2.6928, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 386, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.009, + "combined_pop_emp_density": 0.0054, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.006, + "total_far": 0.0011900, + "floors": 1.000, + "residential_average_lot_size": 6221.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 389, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 142.360, + "combined_pop_emp_density": 16.2142, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.895, + "impervious_roof_percent": 0.895, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 46351.02, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.749, + "total_far": 2.5700000, + "floors": 3.900, + "residential_average_lot_size": 3217.20, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.409 + } + }, + { + "pk": 391, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 18.350, + "combined_pop_emp_density": 22.2156, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.250, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4500000, + "floors": 1.500, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 393, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 29.920, + "combined_pop_emp_density": 54.3581, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.641, + "impervious_roof_percent": 0.641, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.290, + "household_density": 0.000, + "total_far": 1.2000000, + "floors": 1.650, + "residential_average_lot_size": 5538.80, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 395, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.220, + "combined_pop_emp_density": 4.9146, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.056, + "hardscape_percent": 0.920, + "impervious_roof_percent": 0.628, + "softscape_and_landscape_percent": 0.025, + "impervious_hardscape_percent": 0.292, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.5160000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 160.74, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 397, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 176.900, + "combined_pop_emp_density": 177.7248, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 58360.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 3.0000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 341, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 328.605, + "combined_pop_emp_density": 17.9131, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.607, + "impervious_roof_percent": 0.607, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 108447.57, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.075, + "household_density": 0.860, + "total_far": 13.3500000, + "floors": 38.700, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.961 + } + }, + { + "pk": 343, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 22.200, + "combined_pop_emp_density": 17.0889, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 345, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.600, + "combined_pop_emp_density": 1.8513, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2500000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 347, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.500, + "combined_pop_emp_density": 4.9368, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 349, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.590, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.263, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.639, + "household_density": 1.000, + "total_far": 0.3205412, + "floors": 2.000, + "residential_average_lot_size": 99243.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 350, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 16.120, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 2036.34, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.119, + "impervious_roof_percent": 0.064, + "softscape_and_landscape_percent": 0.081, + "impervious_hardscape_percent": 0.055, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.580, + "household_density": 1.000, + "total_far": 0.4312727, + "floors": 2.000, + "residential_average_lot_size": 21400.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 352, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 659.705, + "combined_pop_emp_density": 2.5694, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.481, + "impervious_roof_percent": 0.481, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 208630.08, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.237, + "household_density": 0.991, + "total_far": 17.0550000, + "floors": 68.550, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.456 + } + }, + { + "pk": 354, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 235.775, + "combined_pop_emp_density": 15.4447, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.982, + "impervious_roof_percent": 0.982, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 77803.27, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.450, + "household_density": 0.822, + "total_far": 8.1750000, + "floors": 13.750, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.749 + } + }, + { + "pk": 356, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 508.200, + "combined_pop_emp_density": 14.3246, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.305, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 167706.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.500, + "total_far": 1.4800000, + "floors": 4.850, + "residential_average_lot_size": 6229.95, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.625 + } + }, + { + "pk": 358, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 169.120, + "combined_pop_emp_density": 235.4762, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.371, + "impervious_roof_percent": 0.371, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 55811.27, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.375, + "household_density": 0.080, + "total_far": 2.5850000, + "floors": 4.950, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.080 + } + }, + { + "pk": 360, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 57.740, + "combined_pop_emp_density": 510.4696, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.406, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 19050.26, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.280, + "household_density": 0.000, + "total_far": 3.9000000, + "floors": 9.600, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 362, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 61.310, + "combined_pop_emp_density": 17.6735, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.515, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.335, + "parking_structure_square_feet": 4836.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4800000, + "floors": 1.600, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 364, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 18.350, + "combined_pop_emp_density": 40.3920, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.250, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4500000, + "floors": 1.500, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 366, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.342, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.005, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.014, + "household_density": 1.000, + "total_far": 0.0057122, + "floors": 2.000, + "residential_average_lot_size": 5095631.40, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 367, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 639.940, + "combined_pop_emp_density": 3.5719, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.396, + "impervious_roof_percent": 0.396, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 130680.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 4.1000000, + "floors": 4.600, + "residential_average_lot_size": 7614.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 369, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.000, + "combined_pop_emp_density": 26.9280, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.250, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 371, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 54.6661, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.562, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.287, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.8250000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 373, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.595, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.7090643, + "floors": 2.000, + "residential_average_lot_size": 5472.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 375, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 58.740, + "combined_pop_emp_density": 43.0848, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.555, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.295, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.6400000, + "floors": 4.200, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 377, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 539.145, + "combined_pop_emp_density": 2.9146, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.677, + "impervious_roof_percent": 0.677, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 177913.26, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.245, + "household_density": 0.976, + "total_far": 11.8150000, + "floors": 30.700, + "residential_average_lot_size": 6534.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.385 + } + }, + { + "pk": 379, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.570, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.032, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.090, + "household_density": 1.000, + "total_far": 0.0395065, + "floors": 2.000, + "residential_average_lot_size": 455681.10, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 381, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 529.900, + "combined_pop_emp_density": 1631.4507, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 174867.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.365, + "household_density": 0.436, + "total_far": 39.4900000, + "floors": 76.900, + "residential_average_lot_size": 24005.45, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.516 + } + }, + { + "pk": 383, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 12.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 385, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 40.235, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.293, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 13269.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.105, + "household_density": 1.000, + "total_far": 0.7750000, + "floors": 2.650, + "residential_average_lot_size": 2162.15, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 387, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 111.600, + "combined_pop_emp_density": 91.4193, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.781, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 11046.59, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.460, + "household_density": 0.000, + "total_far": 0.8150000, + "floors": 4.300, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 388, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 80.820, + "combined_pop_emp_density": 22.4849, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.485, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.365, + "parking_structure_square_feet": 6736.46, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4600000, + "floors": 1.750, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 390, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 40.5523, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.570, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.280, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4600000, + "floors": 8.400, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 392, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 63.800, + "combined_pop_emp_density": 33.4900, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 21054.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 394, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 559.880, + "combined_pop_emp_density": 27.6539, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 184753.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 4.6000000, + "floors": 5.100, + "residential_average_lot_size": 6417.50, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 396, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.160, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.352, + "impervious_roof_percent": 0.352, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.690, + "household_density": 1.000, + "total_far": 0.5330653, + "floors": 2.400, + "residential_average_lot_size": 37922.80, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 6, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 3 + } + }, + { + "pk": 7, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 4 + } + }, + { + "pk": 8, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 5 + } + }, + { + "pk": 12, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 9 + } + }, + { + "pk": 13, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 10 + } + }, + { + "pk": 14, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 11 + } + }, + { + "pk": 16, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 13 + } + }, + { + "pk": 17, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 14 + } + }, + { + "pk": 18, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 15 + } + }, + { + "pk": 19, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 16 + } + }, + { + "pk": 20, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 17 + } + }, + { + "pk": 23, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 20 + } + }, + { + "pk": 24, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 21 + } + }, + { + "pk": 25, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 22 + } + }, + { + "pk": 26, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 23 + } + }, + { + "pk": 27, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 24 + } + }, + { + "pk": 28, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 25 + } + }, + { + "pk": 29, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 26 + } + }, + { + "pk": 30, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 27 + } + }, + { + "pk": 33, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 30 + } + }, + { + "pk": 35, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 32 + } + }, + { + "pk": 36, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 33 + } + }, + { + "pk": 39, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 36 + } + }, + { + "pk": 40, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 37 + } + }, + { + "pk": 41, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 38 + } + }, + { + "pk": 42, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 39 + } + }, + { + "pk": 43, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 40 + } + }, + { + "pk": 50, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 47 + } + }, + { + "pk": 53, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 50 + } + }, + { + "pk": 55, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 52 + } + }, + { + "pk": 56, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 53 + } + }, + { + "pk": 57, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 54 + } + }, + { + "pk": 58, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 55 + } + }, + { + "pk": 59, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 56 + } + }, + { + "pk": 60, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 57 + } + }, + { + "pk": 61, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 58 + } + }, + { + "pk": 62, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 59 + } + }, + { + "pk": 63, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 60 + } + }, + { + "pk": 64, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 61 + } + }, + { + "pk": 66, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 63 + } + }, + { + "pk": 67, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 64 + } + }, + { + "pk": 69, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 66 + } + }, + { + "pk": 70, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 67 + } + }, + { + "pk": 71, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 68 + } + }, + { + "pk": 72, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 69 + } + }, + { + "pk": 73, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 70 + } + }, + { + "pk": 74, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 71 + } + }, + { + "pk": 75, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 72 + } + }, + { + "pk": 76, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 73 + } + }, + { + "pk": 77, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 74 + } + }, + { + "pk": 79, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 76 + } + }, + { + "pk": 80, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 77 + } + }, + { + "pk": 81, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 78 + } + }, + { + "pk": 83, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 80 + } + }, + { + "pk": 84, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 81 + } + }, + { + "pk": 85, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 82 + } + }, + { + "pk": 87, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 84 + } + }, + { + "pk": 88, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 85 + } + }, + { + "pk": 89, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 86 + } + }, + { + "pk": 90, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 87 + } + }, + { + "pk": 91, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 88 + } + }, + { + "pk": 92, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 89 + } + }, + { + "pk": 93, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 90 + } + }, + { + "pk": 94, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 91 + } + }, + { + "pk": 95, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 92 + } + }, + { + "pk": 96, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 93 + } + }, + { + "pk": 97, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 94 + } + }, + { + "pk": 99, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 96 + } + }, + { + "pk": 100, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 97 + } + }, + { + "pk": 101, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 98 + } + }, + { + "pk": 102, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 99 + } + }, + { + "pk": 103, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 100 + } + }, + { + "pk": 104, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 101 + } + }, + { + "pk": 105, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 102 + } + }, + { + "pk": 106, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 103 + } + }, + { + "pk": 111, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 108 + } + }, + { + "pk": 112, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 109 + } + }, + { + "pk": 117, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 114 + } + }, + { + "pk": 118, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 115 + } + }, + { + "pk": 119, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 116 + } + }, + { + "pk": 120, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 117 + } + }, + { + "pk": 121, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 118 + } + }, + { + "pk": 122, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 119 + } + }, + { + "pk": 123, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 120 + } + }, + { + "pk": 124, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 121 + } + }, + { + "pk": 125, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 122 + } + }, + { + "pk": 131, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 128 + } + }, + { + "pk": 132, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 129 + } + }, + { + "pk": 134, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 131 + } + }, + { + "pk": 135, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 132 + } + }, + { + "pk": 136, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 133 + } + }, + { + "pk": 138, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 135 + } + }, + { + "pk": 139, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 136 + } + }, + { + "pk": 142, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 139 + } + }, + { + "pk": 143, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 140 + } + }, + { + "pk": 144, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 141 + } + }, + { + "pk": 145, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 142 + } + }, + { + "pk": 146, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 143 + } + }, + { + "pk": 148, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 145 + } + }, + { + "pk": 152, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 149 + } + }, + { + "pk": 153, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 150 + } + }, + { + "pk": 154, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 151 + } + }, + { + "pk": 155, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 152 + } + }, + { + "pk": 156, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 153 + } + }, + { + "pk": 157, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 154 + } + }, + { + "pk": 158, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 155 + } + }, + { + "pk": 159, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 156 + } + }, + { + "pk": 160, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 157 + } + }, + { + "pk": 161, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 158 + } + }, + { + "pk": 162, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 159 + } + }, + { + "pk": 163, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 160 + } + }, + { + "pk": 166, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 163 + } + }, + { + "pk": 167, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 164 + } + }, + { + "pk": 168, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 165 + } + }, + { + "pk": 169, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 166 + } + }, + { + "pk": 170, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 167 + } + }, + { + "pk": 171, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 168 + } + }, + { + "pk": 172, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 169 + } + }, + { + "pk": 173, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 170 + } + }, + { + "pk": 174, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 171 + } + }, + { + "pk": 175, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 172 + } + }, + { + "pk": 176, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 173 + } + }, + { + "pk": 177, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 174 + } + }, + { + "pk": 178, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 175 + } + }, + { + "pk": 179, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 176 + } + }, + { + "pk": 180, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 177 + } + }, + { + "pk": 181, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 178 + } + }, + { + "pk": 182, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 179 + } + }, + { + "pk": 183, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 180 + } + }, + { + "pk": 184, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 181 + } + }, + { + "pk": 185, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 182 + } + }, + { + "pk": 186, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 183 + } + }, + { + "pk": 187, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 184 + } + }, + { + "pk": 188, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 185 + } + }, + { + "pk": 190, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 187 + } + }, + { + "pk": 193, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 190 + } + }, + { + "pk": 201, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 198 + } + }, + { + "pk": 205, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 202 + } + }, + { + "pk": 208, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 205 + } + }, + { + "pk": 209, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 206 + } + }, + { + "pk": 210, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 207 + } + }, + { + "pk": 211, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 208 + } + }, + { + "pk": 212, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 209 + } + }, + { + "pk": 213, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 210 + } + }, + { + "pk": 214, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 211 + } + }, + { + "pk": 215, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 212 + } + }, + { + "pk": 216, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 213 + } + }, + { + "pk": 218, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 215 + } + }, + { + "pk": 219, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 216 + } + }, + { + "pk": 220, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 217 + } + }, + { + "pk": 221, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 218 + } + }, + { + "pk": 224, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 221 + } + }, + { + "pk": 226, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 223 + } + }, + { + "pk": 227, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 224 + } + }, + { + "pk": 229, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 226 + } + }, + { + "pk": 231, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 228 + } + }, + { + "pk": 232, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 229 + } + }, + { + "pk": 233, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 230 + } + }, + { + "pk": 237, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 234 + } + }, + { + "pk": 238, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 235 + } + }, + { + "pk": 239, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 236 + } + }, + { + "pk": 240, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 237 + } + }, + { + "pk": 245, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 242 + } + }, + { + "pk": 246, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 243 + } + }, + { + "pk": 250, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 247 + } + }, + { + "pk": 251, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 248 + } + }, + { + "pk": 254, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 251 + } + }, + { + "pk": 260, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 257 + } + }, + { + "pk": 264, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 261 + } + }, + { + "pk": 265, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 262 + } + }, + { + "pk": 266, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 263 + } + }, + { + "pk": 267, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 264 + } + }, + { + "pk": 268, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 265 + } + }, + { + "pk": 269, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 266 + } + }, + { + "pk": 270, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 267 + } + }, + { + "pk": 273, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 270 + } + }, + { + "pk": 274, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 271 + } + }, + { + "pk": 277, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 274 + } + }, + { + "pk": 282, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 279 + } + }, + { + "pk": 283, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 280 + } + }, + { + "pk": 284, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 281 + } + }, + { + "pk": 288, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 285 + } + }, + { + "pk": 290, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 287 + } + }, + { + "pk": 294, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 291 + } + }, + { + "pk": 296, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 293 + } + }, + { + "pk": 297, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 294 + } + }, + { + "pk": 298, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 295 + } + }, + { + "pk": 299, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 296 + } + }, + { + "pk": 300, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 297 + } + }, + { + "pk": 302, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 299 + } + }, + { + "pk": 303, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 300 + } + }, + { + "pk": 304, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 301 + } + }, + { + "pk": 305, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 302 + } + }, + { + "pk": 306, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 303 + } + }, + { + "pk": 307, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 304 + } + }, + { + "pk": 309, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 306 + } + }, + { + "pk": 311, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 308 + } + }, + { + "pk": 313, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 310 + } + }, + { + "pk": 315, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 312 + } + }, + { + "pk": 316, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 313 + } + }, + { + "pk": 318, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 315 + } + }, + { + "pk": 324, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 321 + } + }, + { + "pk": 328, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 325 + } + }, + { + "pk": 329, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 326 + } + }, + { + "pk": 330, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 327 + } + }, + { + "pk": 331, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 328 + } + }, + { + "pk": 333, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 330 + } + }, + { + "pk": 334, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 331 + } + }, + { + "pk": 335, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 332 + } + }, + { + "pk": 336, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 333 + } + }, + { + "pk": 337, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 334 + } + }, + { + "pk": 338, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 335 + } + }, + { + "pk": 339, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 336 + } + }, + { + "pk": 340, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 337 + } + }, + { + "pk": 341, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 338 + } + }, + { + "pk": 4, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 1 + } + }, + { + "pk": 11, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 8 + } + }, + { + "pk": 48, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 45 + } + }, + { + "pk": 65, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 62 + } + }, + { + "pk": 86, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 83 + } + }, + { + "pk": 114, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 111 + } + }, + { + "pk": 129, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 126 + } + }, + { + "pk": 151, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 148 + } + }, + { + "pk": 164, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 161 + } + }, + { + "pk": 191, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 188 + } + }, + { + "pk": 207, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 204 + } + }, + { + "pk": 217, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 214 + } + }, + { + "pk": 259, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 256 + } + }, + { + "pk": 15, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 12 + } + }, + { + "pk": 34, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 31 + } + }, + { + "pk": 45, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 42 + } + }, + { + "pk": 46, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 43 + } + }, + { + "pk": 47, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 44 + } + }, + { + "pk": 49, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 46 + } + }, + { + "pk": 141, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 138 + } + }, + { + "pk": 165, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 162 + } + }, + { + "pk": 189, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 186 + } + }, + { + "pk": 206, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 203 + } + }, + { + "pk": 225, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 222 + } + }, + { + "pk": 243, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 240 + } + }, + { + "pk": 258, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 255 + } + }, + { + "pk": 281, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 278 + } + }, + { + "pk": 295, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 292 + } + }, + { + "pk": 312, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 309 + } + }, + { + "pk": 332, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 329 + } + }, + { + "pk": 98, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 95 + } + }, + { + "pk": 252, + "model": "main.building", + "fields": { + "tags": [ + 8 + ], + "building_attributes": 249 + } + }, + { + "pk": 344, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 345, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 346, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 3 + ] + } + }, + { + "pk": 347, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 348, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 349, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 350, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 351, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 352, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 353, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 354, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 355, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 356, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 357, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 358, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 359, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 360, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 361, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 362, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 363, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 364, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 367, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 7 + ] + } + }, + { + "pk": 368, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 7 + ] + } + }, + { + "pk": 369, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 370, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 371, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 372, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 7 + ] + } + }, + { + "pk": 373, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 374, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 7 + ] + } + }, + { + "pk": 375, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 7 + ] + } + }, + { + "pk": 376, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 377, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 378, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 343, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 342, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 379, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 380, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 381, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 382, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 383, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 384, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 385, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 386, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 7 + ] + } + }, + { + "pk": 387, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 388, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 389, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 390, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 391, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 392, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 393, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 7 + ] + } + }, + { + "pk": 394, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 7 + ] + } + }, + { + "pk": 395, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 3 + ] + } + }, + { + "pk": 397, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 398, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 399, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 400, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 3 + ] + } + }, + { + "pk": 366, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 365, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 396, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 342, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 6 + ], + "building_attributes": 339 + } + }, + { + "pk": 343, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 1 + ], + "building_attributes": 340 + } + }, + { + "pk": 344, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 1 + ], + "building_attributes": 341 + } + }, + { + "pk": 345, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 2 + ], + "building_attributes": 342 + } + }, + { + "pk": 346, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 3 + ], + "building_attributes": 343 + } + }, + { + "pk": 347, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 344 + } + }, + { + "pk": 348, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 2 + ], + "building_attributes": 345 + } + }, + { + "pk": 349, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 346 + } + }, + { + "pk": 350, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 2 + ], + "building_attributes": 347 + } + }, + { + "pk": 351, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 348 + } + }, + { + "pk": 352, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 349 + } + }, + { + "pk": 353, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 350 + } + }, + { + "pk": 354, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 351 + } + }, + { + "pk": 355, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 352 + } + }, + { + "pk": 356, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 2 + ], + "building_attributes": 353 + } + }, + { + "pk": 357, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 1 + ], + "building_attributes": 354 + } + }, + { + "pk": 358, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 6 + ], + "building_attributes": 355 + } + }, + { + "pk": 359, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 1 + ], + "building_attributes": 356 + } + }, + { + "pk": 360, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 1 + ], + "building_attributes": 357 + } + }, + { + "pk": 361, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 358 + } + }, + { + "pk": 362, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 359 + } + }, + { + "pk": 363, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 360 + } + }, + { + "pk": 364, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 361 + } + }, + { + "pk": 365, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 6 + ], + "building_attributes": 362 + } + }, + { + "pk": 366, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 363 + } + }, + { + "pk": 367, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 7 + ], + "building_attributes": 364 + } + }, + { + "pk": 368, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 7 + ], + "building_attributes": 365 + } + }, + { + "pk": 369, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 366 + } + }, + { + "pk": 370, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 6 + ], + "building_attributes": 367 + } + }, + { + "pk": 371, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 368 + } + }, + { + "pk": 372, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 7 + ], + "building_attributes": 369 + } + }, + { + "pk": 373, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 370 + } + }, + { + "pk": 374, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 7 + ], + "building_attributes": 371 + } + }, + { + "pk": 375, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 7 + ], + "building_attributes": 372 + } + }, + { + "pk": 376, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 373 + } + }, + { + "pk": 377, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 374 + } + }, + { + "pk": 378, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 6 + ], + "building_attributes": 375 + } + }, + { + "pk": 379, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 376 + } + }, + { + "pk": 380, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 377 + } + }, + { + "pk": 381, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 378 + } + }, + { + "pk": 382, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 379 + } + }, + { + "pk": 383, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 2 + ], + "building_attributes": 380 + } + }, + { + "pk": 384, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 1 + ], + "building_attributes": 381 + } + }, + { + "pk": 385, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 382 + } + }, + { + "pk": 386, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 7 + ], + "building_attributes": 383 + } + }, + { + "pk": 387, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 2 + ], + "building_attributes": 384 + } + }, + { + "pk": 388, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 385 + } + }, + { + "pk": 389, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 386 + } + }, + { + "pk": 390, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 387 + } + }, + { + "pk": 391, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 6 + ], + "building_attributes": 388 + } + }, + { + "pk": 392, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 1 + ], + "building_attributes": 389 + } + }, + { + "pk": 393, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 7 + ], + "building_attributes": 390 + } + }, + { + "pk": 394, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 7 + ], + "building_attributes": 391 + } + }, + { + "pk": 395, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 3 + ], + "building_attributes": 392 + } + }, + { + "pk": 396, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 6 + ], + "building_attributes": 393 + } + }, + { + "pk": 397, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 6 + ], + "building_attributes": 394 + } + }, + { + "pk": 398, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 4 + ], + "building_attributes": 395 + } + }, + { + "pk": 399, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 5 + ], + "building_attributes": 396 + } + }, + { + "pk": 400, + "model": "main.buildingtype", + "fields": { + "category": null, + "tags": [ + 3 + ], + "building_attributes": 397 + } + }, + { + "pk": 1, + "model": "main.buildingpercent", + "fields": { + "building": 4, + "buildingtype": 395, + "percent": 1.0000000 + } + }, + { + "pk": 2, + "model": "main.buildingpercent", + "fields": { + "building": 6, + "buildingtype": 346, + "percent": 1.0000000 + } + }, + { + "pk": 3, + "model": "main.buildingpercent", + "fields": { + "building": 7, + "buildingtype": 394, + "percent": 0.5000000 + } + }, + { + "pk": 4, + "model": "main.buildingpercent", + "fields": { + "building": 8, + "buildingtype": 394, + "percent": 0.5000000 + } + }, + { + "pk": 5, + "model": "main.buildingpercent", + "fields": { + "building": 11, + "buildingtype": 352, + "percent": 0.1000000 + } + }, + { + "pk": 6, + "model": "main.buildingpercent", + "fields": { + "building": 12, + "buildingtype": 352, + "percent": 0.1000000 + } + }, + { + "pk": 7, + "model": "main.buildingpercent", + "fields": { + "building": 13, + "buildingtype": 352, + "percent": 0.2000000 + } + }, + { + "pk": 8, + "model": "main.buildingpercent", + "fields": { + "building": 14, + "buildingtype": 352, + "percent": 0.2000000 + } + }, + { + "pk": 9, + "model": "main.buildingpercent", + "fields": { + "building": 15, + "buildingtype": 352, + "percent": 0.2000000 + } + }, + { + "pk": 10, + "model": "main.buildingpercent", + "fields": { + "building": 16, + "buildingtype": 352, + "percent": 0.2000000 + } + }, + { + "pk": 11, + "model": "main.buildingpercent", + "fields": { + "building": 17, + "buildingtype": 373, + "percent": 0.3500000 + } + }, + { + "pk": 12, + "model": "main.buildingpercent", + "fields": { + "building": 18, + "buildingtype": 373, + "percent": 0.3000000 + } + }, + { + "pk": 13, + "model": "main.buildingpercent", + "fields": { + "building": 19, + "buildingtype": 373, + "percent": 0.3500000 + } + }, + { + "pk": 14, + "model": "main.buildingpercent", + "fields": { + "building": 20, + "buildingtype": 344, + "percent": 0.0500000 + } + }, + { + "pk": 15, + "model": "main.buildingpercent", + "fields": { + "building": 23, + "buildingtype": 344, + "percent": 0.0500000 + } + }, + { + "pk": 16, + "model": "main.buildingpercent", + "fields": { + "building": 24, + "buildingtype": 344, + "percent": 0.3000000 + } + }, + { + "pk": 17, + "model": "main.buildingpercent", + "fields": { + "building": 25, + "buildingtype": 344, + "percent": 0.4000000 + } + }, + { + "pk": 18, + "model": "main.buildingpercent", + "fields": { + "building": 26, + "buildingtype": 344, + "percent": 0.1000000 + } + }, + { + "pk": 19, + "model": "main.buildingpercent", + "fields": { + "building": 27, + "buildingtype": 344, + "percent": 0.1000000 + } + }, + { + "pk": 20, + "model": "main.buildingpercent", + "fields": { + "building": 28, + "buildingtype": 351, + "percent": 0.2000000 + } + }, + { + "pk": 21, + "model": "main.buildingpercent", + "fields": { + "building": 29, + "buildingtype": 351, + "percent": 0.1000000 + } + }, + { + "pk": 22, + "model": "main.buildingpercent", + "fields": { + "building": 30, + "buildingtype": 351, + "percent": 0.2000000 + } + }, + { + "pk": 23, + "model": "main.buildingpercent", + "fields": { + "building": 33, + "buildingtype": 351, + "percent": 0.3000000 + } + }, + { + "pk": 24, + "model": "main.buildingpercent", + "fields": { + "building": 34, + "buildingtype": 351, + "percent": 0.2000000 + } + }, + { + "pk": 25, + "model": "main.buildingpercent", + "fields": { + "building": 35, + "buildingtype": 380, + "percent": 0.1000000 + } + }, + { + "pk": 26, + "model": "main.buildingpercent", + "fields": { + "building": 36, + "buildingtype": 380, + "percent": 0.1500000 + } + }, + { + "pk": 27, + "model": "main.buildingpercent", + "fields": { + "building": 39, + "buildingtype": 380, + "percent": 0.0500000 + } + }, + { + "pk": 28, + "model": "main.buildingpercent", + "fields": { + "building": 40, + "buildingtype": 380, + "percent": 0.4000000 + } + }, + { + "pk": 29, + "model": "main.buildingpercent", + "fields": { + "building": 41, + "buildingtype": 380, + "percent": 0.2000000 + } + }, + { + "pk": 30, + "model": "main.buildingpercent", + "fields": { + "building": 42, + "buildingtype": 380, + "percent": 0.1000000 + } + }, + { + "pk": 31, + "model": "main.buildingpercent", + "fields": { + "building": 43, + "buildingtype": 400, + "percent": 1.0000000 + } + }, + { + "pk": 32, + "model": "main.buildingpercent", + "fields": { + "building": 45, + "buildingtype": 358, + "percent": 0.4000000 + } + }, + { + "pk": 33, + "model": "main.buildingpercent", + "fields": { + "building": 46, + "buildingtype": 358, + "percent": 0.4000000 + } + }, + { + "pk": 34, + "model": "main.buildingpercent", + "fields": { + "building": 47, + "buildingtype": 358, + "percent": 0.2000000 + } + }, + { + "pk": 35, + "model": "main.buildingpercent", + "fields": { + "building": 48, + "buildingtype": 378, + "percent": 0.3000000 + } + }, + { + "pk": 36, + "model": "main.buildingpercent", + "fields": { + "building": 49, + "buildingtype": 378, + "percent": 0.3000000 + } + }, + { + "pk": 37, + "model": "main.buildingpercent", + "fields": { + "building": 50, + "buildingtype": 378, + "percent": 0.4000000 + } + }, + { + "pk": 38, + "model": "main.buildingpercent", + "fields": { + "building": 53, + "buildingtype": 385, + "percent": 0.2500000 + } + }, + { + "pk": 39, + "model": "main.buildingpercent", + "fields": { + "building": 55, + "buildingtype": 385, + "percent": 0.3000000 + } + }, + { + "pk": 40, + "model": "main.buildingpercent", + "fields": { + "building": 56, + "buildingtype": 385, + "percent": 0.2000000 + } + }, + { + "pk": 41, + "model": "main.buildingpercent", + "fields": { + "building": 57, + "buildingtype": 385, + "percent": 0.1000000 + } + }, + { + "pk": 42, + "model": "main.buildingpercent", + "fields": { + "building": 58, + "buildingtype": 385, + "percent": 0.1500000 + } + }, + { + "pk": 43, + "model": "main.buildingpercent", + "fields": { + "building": 59, + "buildingtype": 347, + "percent": 0.0500000 + } + }, + { + "pk": 44, + "model": "main.buildingpercent", + "fields": { + "building": 60, + "buildingtype": 347, + "percent": 0.4000000 + } + }, + { + "pk": 45, + "model": "main.buildingpercent", + "fields": { + "building": 61, + "buildingtype": 347, + "percent": 0.4000000 + } + }, + { + "pk": 46, + "model": "main.buildingpercent", + "fields": { + "building": 62, + "buildingtype": 347, + "percent": 0.0500000 + } + }, + { + "pk": 47, + "model": "main.buildingpercent", + "fields": { + "building": 63, + "buildingtype": 347, + "percent": 0.1000000 + } + }, + { + "pk": 48, + "model": "main.buildingpercent", + "fields": { + "building": 64, + "buildingtype": 399, + "percent": 0.2000000 + } + }, + { + "pk": 49, + "model": "main.buildingpercent", + "fields": { + "building": 65, + "buildingtype": 399, + "percent": 0.2000000 + } + }, + { + "pk": 50, + "model": "main.buildingpercent", + "fields": { + "building": 66, + "buildingtype": 399, + "percent": 0.4000000 + } + }, + { + "pk": 51, + "model": "main.buildingpercent", + "fields": { + "building": 67, + "buildingtype": 399, + "percent": 0.2000000 + } + }, + { + "pk": 52, + "model": "main.buildingpercent", + "fields": { + "building": 69, + "buildingtype": 342, + "percent": 0.4000000 + } + }, + { + "pk": 53, + "model": "main.buildingpercent", + "fields": { + "building": 70, + "buildingtype": 342, + "percent": 0.2000000 + } + }, + { + "pk": 54, + "model": "main.buildingpercent", + "fields": { + "building": 71, + "buildingtype": 342, + "percent": 0.0500000 + } + }, + { + "pk": 55, + "model": "main.buildingpercent", + "fields": { + "building": 72, + "buildingtype": 342, + "percent": 0.1000000 + } + }, + { + "pk": 56, + "model": "main.buildingpercent", + "fields": { + "building": 73, + "buildingtype": 342, + "percent": 0.1500000 + } + }, + { + "pk": 57, + "model": "main.buildingpercent", + "fields": { + "building": 74, + "buildingtype": 342, + "percent": 0.1000000 + } + }, + { + "pk": 58, + "model": "main.buildingpercent", + "fields": { + "building": 75, + "buildingtype": 360, + "percent": 0.0500000 + } + }, + { + "pk": 59, + "model": "main.buildingpercent", + "fields": { + "building": 76, + "buildingtype": 360, + "percent": 0.0500000 + } + }, + { + "pk": 60, + "model": "main.buildingpercent", + "fields": { + "building": 77, + "buildingtype": 360, + "percent": 0.0200000 + } + }, + { + "pk": 61, + "model": "main.buildingpercent", + "fields": { + "building": 79, + "buildingtype": 360, + "percent": 0.0700000 + } + }, + { + "pk": 62, + "model": "main.buildingpercent", + "fields": { + "building": 80, + "buildingtype": 360, + "percent": 0.0500000 + } + }, + { + "pk": 63, + "model": "main.buildingpercent", + "fields": { + "building": 81, + "buildingtype": 360, + "percent": 0.1000000 + } + }, + { + "pk": 64, + "model": "main.buildingpercent", + "fields": { + "building": 83, + "buildingtype": 360, + "percent": 0.2000000 + } + }, + { + "pk": 65, + "model": "main.buildingpercent", + "fields": { + "building": 84, + "buildingtype": 360, + "percent": 0.1000000 + } + }, + { + "pk": 66, + "model": "main.buildingpercent", + "fields": { + "building": 85, + "buildingtype": 360, + "percent": 0.0200000 + } + }, + { + "pk": 67, + "model": "main.buildingpercent", + "fields": { + "building": 86, + "buildingtype": 360, + "percent": 0.0200000 + } + }, + { + "pk": 68, + "model": "main.buildingpercent", + "fields": { + "building": 87, + "buildingtype": 360, + "percent": 0.1500000 + } + }, + { + "pk": 69, + "model": "main.buildingpercent", + "fields": { + "building": 88, + "buildingtype": 360, + "percent": 0.1500000 + } + }, + { + "pk": 70, + "model": "main.buildingpercent", + "fields": { + "building": 89, + "buildingtype": 360, + "percent": 0.0200000 + } + }, + { + "pk": 71, + "model": "main.buildingpercent", + "fields": { + "building": 90, + "buildingtype": 361, + "percent": 0.0500000 + } + }, + { + "pk": 72, + "model": "main.buildingpercent", + "fields": { + "building": 91, + "buildingtype": 361, + "percent": 0.0500000 + } + }, + { + "pk": 73, + "model": "main.buildingpercent", + "fields": { + "building": 92, + "buildingtype": 361, + "percent": 0.2000000 + } + }, + { + "pk": 74, + "model": "main.buildingpercent", + "fields": { + "building": 93, + "buildingtype": 361, + "percent": 0.2000000 + } + }, + { + "pk": 75, + "model": "main.buildingpercent", + "fields": { + "building": 94, + "buildingtype": 361, + "percent": 0.3000000 + } + }, + { + "pk": 76, + "model": "main.buildingpercent", + "fields": { + "building": 95, + "buildingtype": 361, + "percent": 0.0500000 + } + }, + { + "pk": 77, + "model": "main.buildingpercent", + "fields": { + "building": 96, + "buildingtype": 361, + "percent": 0.1000000 + } + }, + { + "pk": 78, + "model": "main.buildingpercent", + "fields": { + "building": 97, + "buildingtype": 361, + "percent": 0.0500000 + } + }, + { + "pk": 79, + "model": "main.buildingpercent", + "fields": { + "building": 98, + "buildingtype": 396, + "percent": 0.1500000 + } + }, + { + "pk": 80, + "model": "main.buildingpercent", + "fields": { + "building": 99, + "buildingtype": 396, + "percent": 0.0500000 + } + }, + { + "pk": 81, + "model": "main.buildingpercent", + "fields": { + "building": 100, + "buildingtype": 396, + "percent": 0.0500000 + } + }, + { + "pk": 82, + "model": "main.buildingpercent", + "fields": { + "building": 101, + "buildingtype": 396, + "percent": 0.2500000 + } + }, + { + "pk": 83, + "model": "main.buildingpercent", + "fields": { + "building": 102, + "buildingtype": 396, + "percent": 0.1000000 + } + }, + { + "pk": 84, + "model": "main.buildingpercent", + "fields": { + "building": 103, + "buildingtype": 396, + "percent": 0.2000000 + } + }, + { + "pk": 85, + "model": "main.buildingpercent", + "fields": { + "building": 104, + "buildingtype": 396, + "percent": 0.2000000 + } + }, + { + "pk": 86, + "model": "main.buildingpercent", + "fields": { + "building": 105, + "buildingtype": 392, + "percent": 0.3000000 + } + }, + { + "pk": 87, + "model": "main.buildingpercent", + "fields": { + "building": 106, + "buildingtype": 392, + "percent": 0.4000000 + } + }, + { + "pk": 88, + "model": "main.buildingpercent", + "fields": { + "building": 111, + "buildingtype": 392, + "percent": 0.2000000 + } + }, + { + "pk": 89, + "model": "main.buildingpercent", + "fields": { + "building": 112, + "buildingtype": 392, + "percent": 0.1000000 + } + }, + { + "pk": 90, + "model": "main.buildingpercent", + "fields": { + "building": 114, + "buildingtype": 343, + "percent": 0.2000000 + } + }, + { + "pk": 91, + "model": "main.buildingpercent", + "fields": { + "building": 117, + "buildingtype": 343, + "percent": 0.3000000 + } + }, + { + "pk": 92, + "model": "main.buildingpercent", + "fields": { + "building": 118, + "buildingtype": 343, + "percent": 0.2000000 + } + }, + { + "pk": 93, + "model": "main.buildingpercent", + "fields": { + "building": 119, + "buildingtype": 343, + "percent": 0.3000000 + } + }, + { + "pk": 94, + "model": "main.buildingpercent", + "fields": { + "building": 120, + "buildingtype": 365, + "percent": 0.2000000 + } + }, + { + "pk": 95, + "model": "main.buildingpercent", + "fields": { + "building": 121, + "buildingtype": 365, + "percent": 0.2000000 + } + }, + { + "pk": 96, + "model": "main.buildingpercent", + "fields": { + "building": 122, + "buildingtype": 365, + "percent": 0.1000000 + } + }, + { + "pk": 97, + "model": "main.buildingpercent", + "fields": { + "building": 123, + "buildingtype": 365, + "percent": 0.2000000 + } + }, + { + "pk": 98, + "model": "main.buildingpercent", + "fields": { + "building": 124, + "buildingtype": 365, + "percent": 0.1000000 + } + }, + { + "pk": 99, + "model": "main.buildingpercent", + "fields": { + "building": 125, + "buildingtype": 365, + "percent": 0.2000000 + } + }, + { + "pk": 100, + "model": "main.buildingpercent", + "fields": { + "building": 129, + "buildingtype": 353, + "percent": 0.2000000 + } + }, + { + "pk": 101, + "model": "main.buildingpercent", + "fields": { + "building": 131, + "buildingtype": 353, + "percent": 0.6000000 + } + }, + { + "pk": 102, + "model": "main.buildingpercent", + "fields": { + "building": 132, + "buildingtype": 353, + "percent": 0.2000000 + } + }, + { + "pk": 103, + "model": "main.buildingpercent", + "fields": { + "building": 134, + "buildingtype": 357, + "percent": 0.5000000 + } + }, + { + "pk": 104, + "model": "main.buildingpercent", + "fields": { + "building": 135, + "buildingtype": 357, + "percent": 0.2500000 + } + }, + { + "pk": 105, + "model": "main.buildingpercent", + "fields": { + "building": 136, + "buildingtype": 357, + "percent": 0.2500000 + } + }, + { + "pk": 106, + "model": "main.buildingpercent", + "fields": { + "building": 138, + "buildingtype": 363, + "percent": 0.2000000 + } + }, + { + "pk": 107, + "model": "main.buildingpercent", + "fields": { + "building": 139, + "buildingtype": 363, + "percent": 0.2000000 + } + }, + { + "pk": 108, + "model": "main.buildingpercent", + "fields": { + "building": 141, + "buildingtype": 363, + "percent": 0.2000000 + } + }, + { + "pk": 109, + "model": "main.buildingpercent", + "fields": { + "building": 142, + "buildingtype": 363, + "percent": 0.2000000 + } + }, + { + "pk": 110, + "model": "main.buildingpercent", + "fields": { + "building": 143, + "buildingtype": 363, + "percent": 0.2000000 + } + }, + { + "pk": 111, + "model": "main.buildingpercent", + "fields": { + "building": 144, + "buildingtype": 387, + "percent": 1.0000000 + } + }, + { + "pk": 112, + "model": "main.buildingpercent", + "fields": { + "building": 145, + "buildingtype": 348, + "percent": 1.0000000 + } + }, + { + "pk": 113, + "model": "main.buildingpercent", + "fields": { + "building": 146, + "buildingtype": 345, + "percent": 1.0000000 + } + }, + { + "pk": 114, + "model": "main.buildingpercent", + "fields": { + "building": 148, + "buildingtype": 390, + "percent": 0.2000000 + } + }, + { + "pk": 115, + "model": "main.buildingpercent", + "fields": { + "building": 151, + "buildingtype": 390, + "percent": 0.1000000 + } + }, + { + "pk": 116, + "model": "main.buildingpercent", + "fields": { + "building": 152, + "buildingtype": 390, + "percent": 0.3000000 + } + }, + { + "pk": 117, + "model": "main.buildingpercent", + "fields": { + "building": 153, + "buildingtype": 390, + "percent": 0.3000000 + } + }, + { + "pk": 118, + "model": "main.buildingpercent", + "fields": { + "building": 154, + "buildingtype": 390, + "percent": 0.1000000 + } + }, + { + "pk": 119, + "model": "main.buildingpercent", + "fields": { + "building": 155, + "buildingtype": 377, + "percent": 0.1000000 + } + }, + { + "pk": 120, + "model": "main.buildingpercent", + "fields": { + "building": 156, + "buildingtype": 377, + "percent": 0.1500000 + } + }, + { + "pk": 121, + "model": "main.buildingpercent", + "fields": { + "building": 157, + "buildingtype": 377, + "percent": 0.2500000 + } + }, + { + "pk": 122, + "model": "main.buildingpercent", + "fields": { + "building": 158, + "buildingtype": 377, + "percent": 0.5000000 + } + }, + { + "pk": 123, + "model": "main.buildingpercent", + "fields": { + "building": 159, + "buildingtype": 370, + "percent": 0.2000000 + } + }, + { + "pk": 124, + "model": "main.buildingpercent", + "fields": { + "building": 160, + "buildingtype": 370, + "percent": 0.2000000 + } + }, + { + "pk": 125, + "model": "main.buildingpercent", + "fields": { + "building": 161, + "buildingtype": 370, + "percent": 0.2000000 + } + }, + { + "pk": 126, + "model": "main.buildingpercent", + "fields": { + "building": 162, + "buildingtype": 370, + "percent": 0.2000000 + } + }, + { + "pk": 127, + "model": "main.buildingpercent", + "fields": { + "building": 163, + "buildingtype": 370, + "percent": 0.2000000 + } + }, + { + "pk": 128, + "model": "main.buildingpercent", + "fields": { + "building": 164, + "buildingtype": 359, + "percent": 0.4000000 + } + }, + { + "pk": 129, + "model": "main.buildingpercent", + "fields": { + "building": 165, + "buildingtype": 359, + "percent": 0.3500000 + } + }, + { + "pk": 130, + "model": "main.buildingpercent", + "fields": { + "building": 166, + "buildingtype": 359, + "percent": 0.2500000 + } + }, + { + "pk": 131, + "model": "main.buildingpercent", + "fields": { + "building": 167, + "buildingtype": 397, + "percent": 0.3000000 + } + }, + { + "pk": 132, + "model": "main.buildingpercent", + "fields": { + "building": 168, + "buildingtype": 397, + "percent": 0.3000000 + } + }, + { + "pk": 133, + "model": "main.buildingpercent", + "fields": { + "building": 169, + "buildingtype": 397, + "percent": 0.4000000 + } + }, + { + "pk": 134, + "model": "main.buildingpercent", + "fields": { + "building": 170, + "buildingtype": 391, + "percent": 0.1000000 + } + }, + { + "pk": 135, + "model": "main.buildingpercent", + "fields": { + "building": 171, + "buildingtype": 391, + "percent": 0.1000000 + } + }, + { + "pk": 136, + "model": "main.buildingpercent", + "fields": { + "building": 172, + "buildingtype": 391, + "percent": 0.2000000 + } + }, + { + "pk": 137, + "model": "main.buildingpercent", + "fields": { + "building": 173, + "buildingtype": 391, + "percent": 0.2500000 + } + }, + { + "pk": 138, + "model": "main.buildingpercent", + "fields": { + "building": 174, + "buildingtype": 391, + "percent": 0.2500000 + } + }, + { + "pk": 139, + "model": "main.buildingpercent", + "fields": { + "building": 175, + "buildingtype": 391, + "percent": 0.1000000 + } + }, + { + "pk": 140, + "model": "main.buildingpercent", + "fields": { + "building": 176, + "buildingtype": 389, + "percent": 0.0500000 + } + }, + { + "pk": 141, + "model": "main.buildingpercent", + "fields": { + "building": 177, + "buildingtype": 389, + "percent": 0.0300000 + } + }, + { + "pk": 142, + "model": "main.buildingpercent", + "fields": { + "building": 178, + "buildingtype": 389, + "percent": 0.0500000 + } + }, + { + "pk": 143, + "model": "main.buildingpercent", + "fields": { + "building": 179, + "buildingtype": 389, + "percent": 0.0200000 + } + }, + { + "pk": 144, + "model": "main.buildingpercent", + "fields": { + "building": 180, + "buildingtype": 389, + "percent": 0.0300000 + } + }, + { + "pk": 145, + "model": "main.buildingpercent", + "fields": { + "building": 181, + "buildingtype": 389, + "percent": 0.0200000 + } + }, + { + "pk": 146, + "model": "main.buildingpercent", + "fields": { + "building": 182, + "buildingtype": 389, + "percent": 0.0100000 + } + }, + { + "pk": 147, + "model": "main.buildingpercent", + "fields": { + "building": 183, + "buildingtype": 389, + "percent": 0.2700000 + } + }, + { + "pk": 148, + "model": "main.buildingpercent", + "fields": { + "building": 184, + "buildingtype": 389, + "percent": 0.2000000 + } + }, + { + "pk": 149, + "model": "main.buildingpercent", + "fields": { + "building": 185, + "buildingtype": 389, + "percent": 0.0200000 + } + }, + { + "pk": 150, + "model": "main.buildingpercent", + "fields": { + "building": 186, + "buildingtype": 389, + "percent": 0.0300000 + } + }, + { + "pk": 151, + "model": "main.buildingpercent", + "fields": { + "building": 187, + "buildingtype": 389, + "percent": 0.0100000 + } + }, + { + "pk": 152, + "model": "main.buildingpercent", + "fields": { + "building": 188, + "buildingtype": 389, + "percent": 0.0500000 + } + }, + { + "pk": 153, + "model": "main.buildingpercent", + "fields": { + "building": 189, + "buildingtype": 389, + "percent": 0.0500000 + } + }, + { + "pk": 154, + "model": "main.buildingpercent", + "fields": { + "building": 190, + "buildingtype": 389, + "percent": 0.0500000 + } + }, + { + "pk": 155, + "model": "main.buildingpercent", + "fields": { + "building": 191, + "buildingtype": 389, + "percent": 0.0500000 + } + }, + { + "pk": 156, + "model": "main.buildingpercent", + "fields": { + "building": 193, + "buildingtype": 389, + "percent": 0.0100000 + } + }, + { + "pk": 157, + "model": "main.buildingpercent", + "fields": { + "building": 201, + "buildingtype": 389, + "percent": 0.0500000 + } + }, + { + "pk": 158, + "model": "main.buildingpercent", + "fields": { + "building": 205, + "buildingtype": 369, + "percent": 0.0100000 + } + }, + { + "pk": 159, + "model": "main.buildingpercent", + "fields": { + "building": 206, + "buildingtype": 369, + "percent": 0.0100000 + } + }, + { + "pk": 160, + "model": "main.buildingpercent", + "fields": { + "building": 207, + "buildingtype": 369, + "percent": 0.0100000 + } + }, + { + "pk": 161, + "model": "main.buildingpercent", + "fields": { + "building": 208, + "buildingtype": 369, + "percent": 0.0500000 + } + }, + { + "pk": 162, + "model": "main.buildingpercent", + "fields": { + "building": 209, + "buildingtype": 369, + "percent": 0.0500000 + } + }, + { + "pk": 163, + "model": "main.buildingpercent", + "fields": { + "building": 210, + "buildingtype": 369, + "percent": 0.1500000 + } + }, + { + "pk": 164, + "model": "main.buildingpercent", + "fields": { + "building": 211, + "buildingtype": 369, + "percent": 0.3500000 + } + }, + { + "pk": 165, + "model": "main.buildingpercent", + "fields": { + "building": 212, + "buildingtype": 369, + "percent": 0.3500000 + } + }, + { + "pk": 166, + "model": "main.buildingpercent", + "fields": { + "building": 213, + "buildingtype": 369, + "percent": 0.0100000 + } + }, + { + "pk": 167, + "model": "main.buildingpercent", + "fields": { + "building": 214, + "buildingtype": 369, + "percent": 0.0100000 + } + }, + { + "pk": 168, + "model": "main.buildingpercent", + "fields": { + "building": 215, + "buildingtype": 382, + "percent": 0.1000000 + } + }, + { + "pk": 169, + "model": "main.buildingpercent", + "fields": { + "building": 216, + "buildingtype": 382, + "percent": 0.4000000 + } + }, + { + "pk": 170, + "model": "main.buildingpercent", + "fields": { + "building": 217, + "buildingtype": 382, + "percent": 0.1000000 + } + }, + { + "pk": 171, + "model": "main.buildingpercent", + "fields": { + "building": 218, + "buildingtype": 382, + "percent": 0.4000000 + } + }, + { + "pk": 172, + "model": "main.buildingpercent", + "fields": { + "building": 219, + "buildingtype": 384, + "percent": 0.3500000 + } + }, + { + "pk": 173, + "model": "main.buildingpercent", + "fields": { + "building": 220, + "buildingtype": 384, + "percent": 0.1000000 + } + }, + { + "pk": 174, + "model": "main.buildingpercent", + "fields": { + "building": 221, + "buildingtype": 384, + "percent": 0.5500000 + } + }, + { + "pk": 175, + "model": "main.buildingpercent", + "fields": { + "building": 224, + "buildingtype": 362, + "percent": 0.2000000 + } + }, + { + "pk": 176, + "model": "main.buildingpercent", + "fields": { + "building": 225, + "buildingtype": 362, + "percent": 0.2000000 + } + }, + { + "pk": 177, + "model": "main.buildingpercent", + "fields": { + "building": 226, + "buildingtype": 362, + "percent": 0.2000000 + } + }, + { + "pk": 178, + "model": "main.buildingpercent", + "fields": { + "building": 227, + "buildingtype": 362, + "percent": 0.2000000 + } + }, + { + "pk": 179, + "model": "main.buildingpercent", + "fields": { + "building": 229, + "buildingtype": 362, + "percent": 0.2000000 + } + }, + { + "pk": 180, + "model": "main.buildingpercent", + "fields": { + "building": 231, + "buildingtype": 355, + "percent": 0.1000000 + } + }, + { + "pk": 181, + "model": "main.buildingpercent", + "fields": { + "building": 232, + "buildingtype": 355, + "percent": 0.4500000 + } + }, + { + "pk": 182, + "model": "main.buildingpercent", + "fields": { + "building": 233, + "buildingtype": 355, + "percent": 0.4500000 + } + }, + { + "pk": 183, + "model": "main.buildingpercent", + "fields": { + "building": 237, + "buildingtype": 354, + "percent": 0.3000000 + } + }, + { + "pk": 184, + "model": "main.buildingpercent", + "fields": { + "building": 238, + "buildingtype": 354, + "percent": 0.3000000 + } + }, + { + "pk": 185, + "model": "main.buildingpercent", + "fields": { + "building": 239, + "buildingtype": 354, + "percent": 0.3000000 + } + }, + { + "pk": 186, + "model": "main.buildingpercent", + "fields": { + "building": 240, + "buildingtype": 354, + "percent": 0.1000000 + } + }, + { + "pk": 187, + "model": "main.buildingpercent", + "fields": { + "building": 243, + "buildingtype": 366, + "percent": 0.5500000 + } + }, + { + "pk": 188, + "model": "main.buildingpercent", + "fields": { + "building": 245, + "buildingtype": 366, + "percent": 0.2000000 + } + }, + { + "pk": 189, + "model": "main.buildingpercent", + "fields": { + "building": 246, + "buildingtype": 366, + "percent": 0.2500000 + } + }, + { + "pk": 190, + "model": "main.buildingpercent", + "fields": { + "building": 250, + "buildingtype": 388, + "percent": 0.1000000 + } + }, + { + "pk": 191, + "model": "main.buildingpercent", + "fields": { + "building": 251, + "buildingtype": 388, + "percent": 0.2500000 + } + }, + { + "pk": 192, + "model": "main.buildingpercent", + "fields": { + "building": 252, + "buildingtype": 388, + "percent": 0.4500000 + } + }, + { + "pk": 193, + "model": "main.buildingpercent", + "fields": { + "building": 254, + "buildingtype": 388, + "percent": 0.2000000 + } + }, + { + "pk": 194, + "model": "main.buildingpercent", + "fields": { + "building": 258, + "buildingtype": 368, + "percent": 0.4000000 + } + }, + { + "pk": 195, + "model": "main.buildingpercent", + "fields": { + "building": 259, + "buildingtype": 368, + "percent": 0.3000000 + } + }, + { + "pk": 196, + "model": "main.buildingpercent", + "fields": { + "building": 260, + "buildingtype": 368, + "percent": 0.3000000 + } + }, + { + "pk": 197, + "model": "main.buildingpercent", + "fields": { + "building": 264, + "buildingtype": 381, + "percent": 0.2000000 + } + }, + { + "pk": 198, + "model": "main.buildingpercent", + "fields": { + "building": 265, + "buildingtype": 381, + "percent": 0.2000000 + } + }, + { + "pk": 199, + "model": "main.buildingpercent", + "fields": { + "building": 266, + "buildingtype": 381, + "percent": 0.2000000 + } + }, + { + "pk": 200, + "model": "main.buildingpercent", + "fields": { + "building": 267, + "buildingtype": 381, + "percent": 0.2000000 + } + }, + { + "pk": 201, + "model": "main.buildingpercent", + "fields": { + "building": 268, + "buildingtype": 381, + "percent": 0.2000000 + } + }, + { + "pk": 202, + "model": "main.buildingpercent", + "fields": { + "building": 269, + "buildingtype": 372, + "percent": 0.5000000 + } + }, + { + "pk": 203, + "model": "main.buildingpercent", + "fields": { + "building": 270, + "buildingtype": 372, + "percent": 0.5000000 + } + }, + { + "pk": 204, + "model": "main.buildingpercent", + "fields": { + "building": 273, + "buildingtype": 367, + "percent": 0.5000000 + } + }, + { + "pk": 205, + "model": "main.buildingpercent", + "fields": { + "building": 274, + "buildingtype": 367, + "percent": 0.5000000 + } + }, + { + "pk": 206, + "model": "main.buildingpercent", + "fields": { + "building": 277, + "buildingtype": 386, + "percent": 1.0000000 + } + }, + { + "pk": 207, + "model": "main.buildingpercent", + "fields": { + "building": 281, + "buildingtype": 374, + "percent": 0.7500000 + } + }, + { + "pk": 208, + "model": "main.buildingpercent", + "fields": { + "building": 282, + "buildingtype": 374, + "percent": 0.2500000 + } + }, + { + "pk": 209, + "model": "main.buildingpercent", + "fields": { + "building": 283, + "buildingtype": 393, + "percent": 0.4000000 + } + }, + { + "pk": 210, + "model": "main.buildingpercent", + "fields": { + "building": 284, + "buildingtype": 393, + "percent": 0.6000000 + } + }, + { + "pk": 211, + "model": "main.buildingpercent", + "fields": { + "building": 288, + "buildingtype": 383, + "percent": 1.0000000 + } + }, + { + "pk": 212, + "model": "main.buildingpercent", + "fields": { + "building": 290, + "buildingtype": 356, + "percent": 1.0000000 + } + }, + { + "pk": 213, + "model": "main.buildingpercent", + "fields": { + "building": 294, + "buildingtype": 350, + "percent": 1.0000000 + } + }, + { + "pk": 214, + "model": "main.buildingpercent", + "fields": { + "building": 295, + "buildingtype": 349, + "percent": 0.3500000 + } + }, + { + "pk": 215, + "model": "main.buildingpercent", + "fields": { + "building": 296, + "buildingtype": 349, + "percent": 0.3000000 + } + }, + { + "pk": 216, + "model": "main.buildingpercent", + "fields": { + "building": 297, + "buildingtype": 349, + "percent": 0.2000000 + } + }, + { + "pk": 217, + "model": "main.buildingpercent", + "fields": { + "building": 298, + "buildingtype": 349, + "percent": 0.1000000 + } + }, + { + "pk": 218, + "model": "main.buildingpercent", + "fields": { + "building": 299, + "buildingtype": 349, + "percent": 0.0500000 + } + }, + { + "pk": 219, + "model": "main.buildingpercent", + "fields": { + "building": 300, + "buildingtype": 371, + "percent": 0.1000000 + } + }, + { + "pk": 220, + "model": "main.buildingpercent", + "fields": { + "building": 302, + "buildingtype": 371, + "percent": 0.2000000 + } + }, + { + "pk": 221, + "model": "main.buildingpercent", + "fields": { + "building": 303, + "buildingtype": 371, + "percent": 0.0500000 + } + }, + { + "pk": 222, + "model": "main.buildingpercent", + "fields": { + "building": 304, + "buildingtype": 371, + "percent": 0.0500000 + } + }, + { + "pk": 223, + "model": "main.buildingpercent", + "fields": { + "building": 305, + "buildingtype": 371, + "percent": 0.0500000 + } + }, + { + "pk": 224, + "model": "main.buildingpercent", + "fields": { + "building": 306, + "buildingtype": 371, + "percent": 0.4500000 + } + }, + { + "pk": 225, + "model": "main.buildingpercent", + "fields": { + "building": 307, + "buildingtype": 371, + "percent": 0.1000000 + } + }, + { + "pk": 226, + "model": "main.buildingpercent", + "fields": { + "building": 309, + "buildingtype": 375, + "percent": 1.0000000 + } + }, + { + "pk": 227, + "model": "main.buildingpercent", + "fields": { + "building": 311, + "buildingtype": 379, + "percent": 0.1000000 + } + }, + { + "pk": 228, + "model": "main.buildingpercent", + "fields": { + "building": 312, + "buildingtype": 379, + "percent": 0.2000000 + } + }, + { + "pk": 229, + "model": "main.buildingpercent", + "fields": { + "building": 313, + "buildingtype": 379, + "percent": 0.2500000 + } + }, + { + "pk": 230, + "model": "main.buildingpercent", + "fields": { + "building": 315, + "buildingtype": 379, + "percent": 0.3000000 + } + }, + { + "pk": 231, + "model": "main.buildingpercent", + "fields": { + "building": 316, + "buildingtype": 379, + "percent": 0.0500000 + } + }, + { + "pk": 232, + "model": "main.buildingpercent", + "fields": { + "building": 318, + "buildingtype": 379, + "percent": 0.1000000 + } + }, + { + "pk": 233, + "model": "main.buildingpercent", + "fields": { + "building": 324, + "buildingtype": 376, + "percent": 1.0000000 + } + }, + { + "pk": 234, + "model": "main.buildingpercent", + "fields": { + "building": 328, + "buildingtype": 364, + "percent": 0.1000000 + } + }, + { + "pk": 235, + "model": "main.buildingpercent", + "fields": { + "building": 329, + "buildingtype": 364, + "percent": 0.0500000 + } + }, + { + "pk": 236, + "model": "main.buildingpercent", + "fields": { + "building": 330, + "buildingtype": 364, + "percent": 0.2000000 + } + }, + { + "pk": 237, + "model": "main.buildingpercent", + "fields": { + "building": 331, + "buildingtype": 364, + "percent": 0.0500000 + } + }, + { + "pk": 238, + "model": "main.buildingpercent", + "fields": { + "building": 332, + "buildingtype": 364, + "percent": 0.0500000 + } + }, + { + "pk": 239, + "model": "main.buildingpercent", + "fields": { + "building": 333, + "buildingtype": 364, + "percent": 0.0500000 + } + }, + { + "pk": 240, + "model": "main.buildingpercent", + "fields": { + "building": 334, + "buildingtype": 364, + "percent": 0.0500000 + } + }, + { + "pk": 241, + "model": "main.buildingpercent", + "fields": { + "building": 335, + "buildingtype": 364, + "percent": 0.2000000 + } + }, + { + "pk": 242, + "model": "main.buildingpercent", + "fields": { + "building": 336, + "buildingtype": 364, + "percent": 0.2000000 + } + }, + { + "pk": 243, + "model": "main.buildingpercent", + "fields": { + "building": 337, + "buildingtype": 364, + "percent": 0.0500000 + } + }, + { + "pk": 244, + "model": "main.buildingpercent", + "fields": { + "building": 338, + "buildingtype": 398, + "percent": 0.5000000 + } + }, + { + "pk": 245, + "model": "main.buildingpercent", + "fields": { + "building": 339, + "buildingtype": 398, + "percent": 0.1000000 + } + }, + { + "pk": 246, + "model": "main.buildingpercent", + "fields": { + "building": 340, + "buildingtype": 398, + "percent": 0.2000000 + } + }, + { + "pk": 247, + "model": "main.buildingpercent", + "fields": { + "building": 341, + "buildingtype": 398, + "percent": 0.2000000 + } + } +] \ No newline at end of file diff --git a/footprint/client/configuration/default/built_form/json_fixtures/building_uses_and_attributes.json b/footprint/client/configuration/default/built_form/json_fixtures/building_uses_and_attributes.json new file mode 100644 index 000000000..f8e7c741e --- /dev/null +++ b/footprint/client/configuration/default/built_form/json_fixtures/building_uses_and_attributes.json @@ -0,0 +1,37897 @@ +[ + { + "pk": 24, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Construction Utilities", + "description": null + } + }, + { + "pk": 25, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Public Admin", + "description": null + } + }, + { + "pk": 18, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Manufacturing", + "description": null + } + }, + { + "pk": 5, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Office", + "description": null + } + }, + { + "pk": 21, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Multifamily 2 To 4", + "description": null + } + }, + { + "pk": 7, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Restaurant", + "description": null + } + }, + { + "pk": 16, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Medical Services", + "description": null + } + }, + { + "pk": 20, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Transport Warehouse", + "description": null + } + }, + { + "pk": 13, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Arts Entertainment", + "description": null + } + }, + { + "pk": 10, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Other Services", + "description": null + } + }, + { + "pk": 9, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Retail Services", + "description": null + } + }, + { + "pk": 4, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Industrial", + "description": null + } + }, + { + "pk": 14, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Multifamily 5 Plus", + "description": null + } + }, + { + "pk": 19, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Wholesale", + "description": null + } + }, + { + "pk": 8, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Education Services", + "description": null + } + }, + { + "pk": 3, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Retail", + "description": null + } + }, + { + "pk": 26, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Single Family Small Lot", + "description": null + } + }, + { + "pk": 15, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Office Services", + "description": null + } + }, + { + "pk": 23, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Extraction", + "description": null + } + }, + { + "pk": 11, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Single Family Large Lot", + "description": null + } + }, + { + "pk": 22, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Agriculture", + "description": null + } + }, + { + "pk": 6, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Agricultural", + "description": null + } + }, + { + "pk": 2, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Residential", + "description": null + } + }, + { + "pk": 17, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Accommodation", + "description": null + } + }, + { + "pk": 12, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Attached Single Family", + "description": null + } + }, + { + "pk": 2, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 25.900, + "combined_pop_emp_density": 39.0717, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.7000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 3, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 22.200, + "combined_pop_emp_density": 17.0889, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 5, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 23.700, + "combined_pop_emp_density": 29.6208, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 6, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 18.500, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 7, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.000, + "combined_pop_emp_density": 49.3680, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 8, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.272, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.890, + "household_density": 1.000, + "total_far": 0.2800000, + "floors": 2.000, + "residential_average_lot_size": 10000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 9, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 8.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.141, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 0.1500000, + "floors": 2.000, + "residential_average_lot_size": 20000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 11, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 8.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.191, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.450, + "household_density": 1.000, + "total_far": 0.2500000, + "floors": 2.000, + "residential_average_lot_size": 20000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 12, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.299, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.3279257, + "floors": 2.000, + "residential_average_lot_size": 9801.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 13, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 16.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.365, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.4764468, + "floors": 2.000, + "residential_average_lot_size": 10402.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 14, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 18664.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 16, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 45.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.267, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 14850.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 3.000, + "residential_average_lot_size": 1409.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 17, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 161.200, + "combined_pop_emp_density": 7.0301, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.150, + "impervious_roof_percent": 0.150, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 53196.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.890, + "total_far": 6.6000000, + "floors": 49.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.225 + } + }, + { + "pk": 18, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.000, + "combined_pop_emp_density": 367.2433, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.256, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 6600.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.030, + "household_density": 0.640, + "total_far": 12.8000000, + "floors": 50.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.600 + } + }, + { + "pk": 19, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 10.000, + "combined_pop_emp_density": 158.5492, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.120, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 3300.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.550, + "total_far": 6.0000000, + "floors": 50.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.375 + } + }, + { + "pk": 21, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 391.100, + "combined_pop_emp_density": 10.0813, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.400, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 129072.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.920, + "total_far": 15.6000000, + "floors": 41.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.300 + } + }, + { + "pk": 22, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 417.600, + "combined_pop_emp_density": 5.6609, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 137817.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.030, + "household_density": 0.940, + "total_far": 11.8000000, + "floors": 40.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.350 + } + }, + { + "pk": 23, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 294.000, + "combined_pop_emp_density": 6.5398, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 97029.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.920, + "total_far": 8.5000000, + "floors": 23.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.300 + } + }, + { + "pk": 24, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 1632.8602, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.220, + "total_far": 20.1000000, + "floors": 39.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.550 + } + }, + { + "pk": 25, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 190.600, + "combined_pop_emp_density": 2395.2082, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 62898.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 16.5000000, + "floors": 33.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 27, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 62.400, + "combined_pop_emp_density": 455.0866, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.170, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 20588.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 3.4000000, + "floors": 20.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 28, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 333.200, + "combined_pop_emp_density": 1180.0118, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.219, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 109967.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 9.0000000, + "floors": 41.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 29, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 222.200, + "combined_pop_emp_density": 754.7737, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.223, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 73311.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 6.0000000, + "floors": 27.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 30, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 477.000, + "combined_pop_emp_density": 1467.4754, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 157410.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 11.0000000, + "floors": 31.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 31, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 351.700, + "combined_pop_emp_density": 2758.1186, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 116076.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 19.0000000, + "floors": 31.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 33, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 400.700, + "combined_pop_emp_density": 2.7916, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.120, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 132233.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.970, + "total_far": 5.5000000, + "floors": 46.000, + "residential_average_lot_size": 43560.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.425 + } + }, + { + "pk": 34, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 278.600, + "combined_pop_emp_density": 2.5255, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 91948.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.990, + "total_far": 7.5000000, + "floors": 25.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.475 + } + }, + { + "pk": 35, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 258.600, + "combined_pop_emp_density": 2.6916, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.150, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 85334.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.970, + "total_far": 6.0000000, + "floors": 40.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.425 + } + }, + { + "pk": 36, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 199.200, + "combined_pop_emp_density": 4.0338, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.188, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 65735.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.940, + "total_far": 6.0000000, + "floors": 32.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.350 + } + }, + { + "pk": 38, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 405.800, + "combined_pop_emp_density": 2.8491, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 133897.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.980, + "total_far": 12.8000000, + "floors": 27.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 39, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 388.900, + "combined_pop_emp_density": 2.5729, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.970, + "impervious_roof_percent": 0.970, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 128333.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.250, + "household_density": 0.990, + "total_far": 11.9000000, + "floors": 24.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.475 + } + }, + { + "pk": 40, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 176.900, + "combined_pop_emp_density": 177.7248, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 58360.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 3.0000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 41, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 74.100, + "combined_pop_emp_density": 118.4832, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 2.0000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 42, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 248.200, + "combined_pop_emp_density": 1151.1720, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 81909.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 17.1000000, + "floors": 40.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 43, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 87.100, + "combined_pop_emp_density": 430.8480, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 28749.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 6.4000000, + "floors": 27.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 45, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.300, + "combined_pop_emp_density": 67.3200, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 46, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.300, + "combined_pop_emp_density": 13.4640, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 47, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 62.400, + "combined_pop_emp_density": 47.1240, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.7000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 48, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 50.500, + "combined_pop_emp_density": 15.8162, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 49, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 42.800, + "combined_pop_emp_density": 11.2400, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 50, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 6.000, + "combined_pop_emp_density": 23.1412, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 52, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 27.7695, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 53, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 2.700, + "combined_pop_emp_density": 21.0172, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 54, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 81.000, + "combined_pop_emp_density": 9.6048, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 55, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 43.500, + "combined_pop_emp_density": 14.5658, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 56, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 25.200, + "combined_pop_emp_density": 9.2565, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 57, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.000, + "combined_pop_emp_density": 8.2280, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 58, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.100, + "combined_pop_emp_density": 6.0042, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 59, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.000, + "combined_pop_emp_density": 14.8264, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 61, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.318, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.650, + "household_density": 1.000, + "total_far": 0.3733333, + "floors": 2.000, + "residential_average_lot_size": 7500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 62, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.398, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.650, + "household_density": 1.000, + "total_far": 0.5333333, + "floors": 2.000, + "residential_average_lot_size": 7500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 63, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 16.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.880, + "impervious_roof_percent": 0.880, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.700, + "household_density": 1.000, + "total_far": 0.6126634, + "floors": 3.000, + "residential_average_lot_size": 7802.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 64, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.398, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.5333333, + "floors": 2.000, + "residential_average_lot_size": 7500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 66, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 26.700, + "combined_pop_emp_density": 5.8080, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 67, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 103.100, + "combined_pop_emp_density": 9.8736, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 68, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 91.700, + "combined_pop_emp_density": 14.8104, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 69, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 95.200, + "combined_pop_emp_density": 13.4219, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 70, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 57.200, + "combined_pop_emp_density": 10.4219, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 71, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 41.800, + "combined_pop_emp_density": 11.6160, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 73, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 109.700, + "combined_pop_emp_density": 8.2635, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 36194.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.850, + "total_far": 3.5000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.125 + } + }, + { + "pk": 74, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 45.600, + "combined_pop_emp_density": 3.3041, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 15031.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.940, + "total_far": 3.4000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.350 + } + }, + { + "pk": 75, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 114.200, + "combined_pop_emp_density": 69.5955, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.750, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 37675.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.400, + "total_far": 3.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.000 + } + }, + { + "pk": 76, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 154.900, + "combined_pop_emp_density": 4.1208, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 51126.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.900, + "total_far": 2.4000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 78, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 240.700, + "combined_pop_emp_density": 5.3680, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 79420.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.900, + "total_far": 4.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 79, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 70.800, + "combined_pop_emp_density": 11.6187, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.250, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 23359.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.750, + "total_far": 2.0000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.875 + } + }, + { + "pk": 80, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 239.100, + "combined_pop_emp_density": 31.3312, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 78903.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.250, + "household_density": 0.700, + "total_far": 5.0000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.750 + } + }, + { + "pk": 82, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 107.500, + "combined_pop_emp_density": 4.5885, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 35484.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.900, + "total_far": 3.0000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 83, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 171.900, + "combined_pop_emp_density": 3.4326, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 56740.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.930, + "total_far": 2.9000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.325 + } + }, + { + "pk": 85, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 196.400, + "combined_pop_emp_density": 2.5070, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 64808.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.990, + "total_far": 4.1000000, + "floors": 7.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.475 + } + }, + { + "pk": 86, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 154.000, + "combined_pop_emp_density": 4.4042, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 50820.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.900, + "total_far": 3.2000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 87, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 74.100, + "combined_pop_emp_density": 275.2862, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 24437.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.400, + "household_density": 0.000, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 88, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 177.900, + "combined_pop_emp_density": 476.0486, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.300, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 58720.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 4.5000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 89, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 250.000, + "combined_pop_emp_density": 488.4281, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 82500.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.400, + "household_density": 0.000, + "total_far": 3.1000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 91, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 155.000, + "combined_pop_emp_density": 128.3568, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.260, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 51150.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 1.3000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 92, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 78.600, + "combined_pop_emp_density": 242.0019, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.934, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 25928.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 2.8000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 93, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.000, + "combined_pop_emp_density": 105.7886, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.250, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12218.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 94, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.800, + "combined_pop_emp_density": 123.4200, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.250, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12463.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 95, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 78.000, + "combined_pop_emp_density": 133.9989, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.400, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.9000000, + "floors": 2.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 96, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 59.000, + "combined_pop_emp_density": 56.4206, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 13895.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 97, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 29.600, + "combined_pop_emp_density": 56.4206, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.8000000, + "floors": 1.000, + "residential_average_lot_size": 7117.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 98, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.800, + "combined_pop_emp_density": 91.6834, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.3000000, + "floors": 2.000, + "residential_average_lot_size": 6956.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 99, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.300, + "combined_pop_emp_density": 63.4731, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.9000000, + "floors": 1.000, + "residential_average_lot_size": 3600.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 101, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 57.200, + "combined_pop_emp_density": 49.3680, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.300, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 102, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.000, + "combined_pop_emp_density": 11.5358, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 16400.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.780, + "total_far": 2.2000000, + "floors": 5.000, + "residential_average_lot_size": 6280.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.950 + } + }, + { + "pk": 103, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 174.700, + "combined_pop_emp_density": 28.7022, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 57649.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.690, + "total_far": 2.9000000, + "floors": 3.000, + "residential_average_lot_size": 3333.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.725 + } + }, + { + "pk": 104, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 75.600, + "combined_pop_emp_density": 140.9391, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 24941.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 2.4000000, + "floors": 3.000, + "residential_average_lot_size": 4012.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 106, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 45.500, + "combined_pop_emp_density": 16.2826, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.367, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 15020.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.450, + "total_far": 1.1000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.125 + } + }, + { + "pk": 107, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 4.3182, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.166, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 5752.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.700, + "total_far": 0.5000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.750 + } + }, + { + "pk": 108, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 130.900, + "combined_pop_emp_density": 11.5160, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 43180.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.810, + "total_far": 2.8000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.025 + } + }, + { + "pk": 109, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 295.000, + "combined_pop_emp_density": 10.8875, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.700, + "impervious_roof_percent": 0.700, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 97350.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.770, + "total_far": 1.9000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.925 + } + }, + { + "pk": 110, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 28.100, + "combined_pop_emp_density": 24.7700, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.200, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 9271.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.400, + "total_far": 0.8000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.000 + } + }, + { + "pk": 112, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 73.600, + "combined_pop_emp_density": 33.7170, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.520, + "total_far": 1.9000000, + "floors": 2.000, + "residential_average_lot_size": 9152.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.300 + } + }, + { + "pk": 113, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 73.600, + "combined_pop_emp_density": 33.7170, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.520, + "total_far": 1.9000000, + "floors": 2.000, + "residential_average_lot_size": 9152.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.300 + } + }, + { + "pk": 114, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 32.1733, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.520, + "total_far": 1.9000000, + "floors": 2.000, + "residential_average_lot_size": 9152.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.300 + } + }, + { + "pk": 115, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 33.7982, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.520, + "total_far": 2.0000000, + "floors": 2.000, + "residential_average_lot_size": 8958.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.300 + } + }, + { + "pk": 117, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.200, + "combined_pop_emp_density": 44.0398, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 1995.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.9000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 118, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.600, + "combined_pop_emp_density": 19.8808, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 9339.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 119, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 55.500, + "combined_pop_emp_density": 9.2565, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 120, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 46.300, + "combined_pop_emp_density": 7.5533, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 121, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 51.800, + "combined_pop_emp_density": 14.8104, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 123, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.347, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.4545455, + "floors": 2.000, + "residential_average_lot_size": 5500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 124, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 19.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 13166.01, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.597, + "impervious_roof_percent": 0.321, + "softscape_and_landscape_percent": 0.403, + "impervious_hardscape_percent": 0.276, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.4033333, + "floors": 2.000, + "residential_average_lot_size": 4500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 125, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.320, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.850, + "household_density": 1.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 5500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 126, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 9655.07, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.597, + "impervious_roof_percent": 0.321, + "softscape_and_landscape_percent": 0.403, + "impervious_hardscape_percent": 0.276, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 128, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.338, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 1.000, + "total_far": 0.4363636, + "floors": 2.000, + "residential_average_lot_size": 5500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 129, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.293, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.3472727, + "floors": 2.000, + "residential_average_lot_size": 5500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 130, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 105.900, + "combined_pop_emp_density": 11.3539, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.200, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 34930.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.800, + "total_far": 3.0000000, + "floors": 15.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.000 + } + }, + { + "pk": 131, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 248.300, + "combined_pop_emp_density": 4.9036, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 81935.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.940, + "total_far": 9.1000000, + "floors": 16.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.350 + } + }, + { + "pk": 132, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 223.200, + "combined_pop_emp_density": 13.3097, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 73669.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.840, + "total_far": 7.4000000, + "floors": 12.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.100 + } + }, + { + "pk": 133, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 223.300, + "combined_pop_emp_density": 92.5185, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 73672.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.570, + "total_far": 7.1000000, + "floors": 11.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.425 + } + }, + { + "pk": 135, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 85.200, + "combined_pop_emp_density": 1135.4640, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.511, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 28102.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 4.6000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 136, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 85.200, + "combined_pop_emp_density": 923.1322, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.511, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 28102.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 4.6000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 137, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 92.600, + "combined_pop_emp_density": 560.8957, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.357, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 30546.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 5.0000000, + "floors": 14.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 138, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 55.500, + "combined_pop_emp_density": 286.9779, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.272, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 18327.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 3.0000000, + "floors": 11.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 139, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.700, + "combined_pop_emp_density": 592.4160, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.622, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 10454.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 5.6000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 141, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 2.6928, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 142, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.600, + "combined_pop_emp_density": 1.8513, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2500000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 143, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 1.9747, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 144, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 38.900, + "combined_pop_emp_density": 21.8189, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.395, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 145, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 118.000, + "combined_pop_emp_density": 86.3940, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.766, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 11680.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.7000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 146, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 51.800, + "combined_pop_emp_density": 42.3154, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.526, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 148, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 45.400, + "combined_pop_emp_density": 25.9182, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.358, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 4490.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 0.3500000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 150, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 129.600, + "combined_pop_emp_density": 123.4200, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.887, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12829.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 151, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 49.200, + "combined_pop_emp_density": 49.3680, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.394, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 4868.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 152, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 62.000, + "combined_pop_emp_density": 11.6508, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 153, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 43.000, + "combined_pop_emp_density": 12.4407, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 154, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 54.000, + "combined_pop_emp_density": 16.2421, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 155, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 44.800, + "combined_pop_emp_density": 16.4560, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 156, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 528.000, + "combined_pop_emp_density": 3.0492, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.875, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 174240.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 3.5000000, + "floors": 4.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 157, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 660.000, + "combined_pop_emp_density": 3.9204, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 217800.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 4.5000000, + "floors": 5.000, + "residential_average_lot_size": 7117.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 158, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 435.600, + "combined_pop_emp_density": 2.1780, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 2.5000000, + "floors": 3.000, + "residential_average_lot_size": 19000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 160, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 792.000, + "combined_pop_emp_density": 4.7916, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.917, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 261360.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 5.5000000, + "floors": 6.000, + "residential_average_lot_size": 6956.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 163, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 660.000, + "combined_pop_emp_density": 23.4656, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 217800.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.500, + "total_far": 2.4000000, + "floors": 6.000, + "residential_average_lot_size": 6956.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.250 + } + }, + { + "pk": 164, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 726.000, + "combined_pop_emp_density": 34.7961, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 239580.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 5.5000000, + "floors": 6.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 165, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 632.000, + "combined_pop_emp_density": 39.6396, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 208560.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 6.5000000, + "floors": 7.000, + "residential_average_lot_size": 7117.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 166, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 381.200, + "combined_pop_emp_density": 21.7800, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 125779.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 2.5000000, + "floors": 3.000, + "residential_average_lot_size": 6956.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 167, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 64.800, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 169, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 108.600, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 10755.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 170, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 93.800, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 9285.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 171, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 65.600, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 6490.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 172, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 62.900, + "combined_pop_emp_density": 13.4594, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 173, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 174, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.100, + "combined_pop_emp_density": 0.0335, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.003, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0020000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 175, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 177, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0293, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 178, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0542, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.020, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.050 + } + }, + { + "pk": 179, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0791, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.030, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.075 + } + }, + { + "pk": 180, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 181, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 183, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 184, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0791, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.030, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.075 + } + }, + { + "pk": 185, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0293, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 186, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0791, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.030, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.075 + } + }, + { + "pk": 188, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0293, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 189, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 190, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.400, + "combined_pop_emp_density": 0.0566, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.016, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0130000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 191, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0038, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 192, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0293, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 193, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 194, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 196, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0521, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 197, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0041, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.000, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 198, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 199, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0043, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 200, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.400, + "combined_pop_emp_density": 0.1699, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.016, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0130000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 201, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0741, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 203, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.016, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.040, + "household_density": 1.000, + "total_far": 0.0160698, + "floors": 2.000, + "residential_average_lot_size": 217800.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 204, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.052, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.120, + "household_density": 1.000, + "total_far": 0.0535660, + "floors": 2.000, + "residential_average_lot_size": 65340.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 205, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.500, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.008, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.020, + "household_density": 1.000, + "total_far": 0.0080349, + "floors": 2.000, + "residential_average_lot_size": 435600.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 206, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.009, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.020, + "household_density": 1.000, + "total_far": 0.0097222, + "floors": 2.000, + "residential_average_lot_size": 360000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 207, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.004, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.010, + "household_density": 1.000, + "total_far": 0.0040174, + "floors": 2.000, + "residential_average_lot_size": 871200.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 209, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.003, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.010, + "household_density": 1.000, + "total_far": 0.0038462, + "floors": 2.000, + "residential_average_lot_size": 910000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 210, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.016, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.040, + "household_density": 1.000, + "total_far": 0.0166667, + "floors": 2.000, + "residential_average_lot_size": 210000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 211, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 2.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.034, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.080, + "household_density": 1.000, + "total_far": 0.0350000, + "floors": 2.000, + "residential_average_lot_size": 100000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 212, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.028, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.0312500, + "floors": 2.000, + "residential_average_lot_size": 80000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 214, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.500, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.103, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 0.1388146, + "floors": 2.000, + "residential_average_lot_size": 28801.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 215, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.031, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.0375000, + "floors": 2.000, + "residential_average_lot_size": 80000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 216, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 295.000, + "combined_pop_emp_density": 466.8331, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 97350.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.600, + "total_far": 26.8000000, + "floors": 100.000, + "residential_average_lot_size": 85.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.500 + } + }, + { + "pk": 217, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 543.000, + "combined_pop_emp_density": 491.3381, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 179190.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.400, + "household_density": 0.450, + "total_far": 18.4000000, + "floors": 56.000, + "residential_average_lot_size": 174.90, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.125 + } + }, + { + "pk": 218, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 677.000, + "combined_pop_emp_density": 3418.0967, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 223410.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.400, + "household_density": 0.330, + "total_far": 51.4000000, + "floors": 66.000, + "residential_average_lot_size": 43560.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.825 + } + }, + { + "pk": 220, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1864.300, + "combined_pop_emp_density": 3770.1206, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.300, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 615205.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 26.5000000, + "floors": 83.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 221, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1498.400, + "combined_pop_emp_density": 2969.5970, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 494485.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 21.3000000, + "floors": 73.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 222, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1407.000, + "combined_pop_emp_density": 2788.3540, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 464306.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.300, + "household_density": 0.000, + "total_far": 20.0000000, + "floors": 55.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 223, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 952.000, + "combined_pop_emp_density": 4723.3209, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 314160.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 33.2000000, + "floors": 62.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 224, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 534.000, + "combined_pop_emp_density": 1052.7884, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.200, + "impervious_roof_percent": 0.200, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 176220.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 7.4000000, + "floors": 55.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 225, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 335.100, + "combined_pop_emp_density": 2575.0635, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 110578.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 18.1000000, + "floors": 58.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 227, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 264.600, + "combined_pop_emp_density": 4.5798, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.164, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 87305.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.030, + "household_density": 0.940, + "total_far": 9.0000000, + "floors": 55.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.350 + } + }, + { + "pk": 228, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 545.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.400, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 179852.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.030, + "household_density": 1.000, + "total_far": 18.9000000, + "floors": 60.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 229, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 672.400, + "combined_pop_emp_density": 2.9733, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.331, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 221899.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.020, + "household_density": 0.980, + "total_far": 15.9000000, + "floors": 48.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 230, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 672.500, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 201755.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 17.8000000, + "floors": 91.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 232, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 21.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.415, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 4000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 233, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.383, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.4500000, + "floors": 2.000, + "residential_average_lot_size": 4000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 234, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.497, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.6793400, + "floors": 2.000, + "residential_average_lot_size": 4182.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 235, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.462, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.6122931, + "floors": 2.000, + "residential_average_lot_size": 4230.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 236, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.344, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.3750000, + "floors": 2.000, + "residential_average_lot_size": 4000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 238, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 113.800, + "combined_pop_emp_density": 2.6108, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 37538.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.970, + "total_far": 3.0000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.425 + } + }, + { + "pk": 239, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 156.900, + "combined_pop_emp_density": 3.3747, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.450, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 51779.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.950, + "total_far": 2.7000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.375 + } + }, + { + "pk": 240, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 74.100, + "combined_pop_emp_density": 3.2479, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 24437.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.920, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.300 + } + }, + { + "pk": 241, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 88.900, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.650, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 29337.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.450, + "household_density": 1.000, + "total_far": 2.6000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 243, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 127.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.970, + "impervious_roof_percent": 0.970, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 42048.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 245, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 59.500, + "combined_pop_emp_density": 3.7310, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 19637.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.900, + "total_far": 2.0000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 246, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 249.900, + "combined_pop_emp_density": 5.5823, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 82475.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.900, + "total_far": 4.5000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.250 + } + }, + { + "pk": 247, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 60.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 19800.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 1452.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 249, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 32.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.234, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 10649.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 0.7000000, + "floors": 3.000, + "residential_average_lot_size": 2519.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 250, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 18668.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.450, + "household_density": 1.000, + "total_far": 1.2000000, + "floors": 3.000, + "residential_average_lot_size": 2219.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 251, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 44.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 14520.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 1917.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 252, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 48.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.550, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 15972.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.400, + "household_density": 1.000, + "total_far": 1.1000000, + "floors": 2.000, + "residential_average_lot_size": 1742.40, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 254, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 60.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.200, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 19827.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 4.000, + "residential_average_lot_size": 1450.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 255, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.500, + "combined_pop_emp_density": 8.2280, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 256, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 7.400, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 257, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.200, + "combined_pop_emp_density": 8.2280, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 258, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 111.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 36655.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.250, + "household_density": 1.000, + "total_far": 1.5000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 260, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 50.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.350, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 16531.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 0.7000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 261, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 109.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 36034.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 262, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 98.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.800, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 32353.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 263, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 48.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.650, + "impervious_roof_percent": 0.650, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 4764.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 265, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 112.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.750, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 37049.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 266, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 8.100, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 267, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.900, + "combined_pop_emp_density": 32.9120, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 268, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.100, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 269, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 9.300, + "combined_pop_emp_density": 20.5700, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2500000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 271, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 23.700, + "combined_pop_emp_density": 49.3680, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 272, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 27.800, + "combined_pop_emp_density": 65.8240, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 273, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.000, + "combined_pop_emp_density": 82.2800, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 274, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 12.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 275, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 41.1400, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 277, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 21.8536, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 278, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 67.1405, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 279, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 19.5497, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 280, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 69.1152, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.7000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 281, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 10.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 283, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.200, + "combined_pop_emp_density": 4.4431, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 284, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.500, + "combined_pop_emp_density": 5.9242, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 285, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.100, + "combined_pop_emp_density": 4.4431, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 286, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.100, + "combined_pop_emp_density": 4.4431, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 288, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.500, + "combined_pop_emp_density": 3.9494, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 289, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.600, + "combined_pop_emp_density": 4.9368, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 290, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.500, + "combined_pop_emp_density": 4.9368, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 291, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.500, + "combined_pop_emp_density": 4.9368, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 292, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 150.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 49500.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 8.0000000, + "floors": 14.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 294, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 110.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.590, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 36300.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 5.9000000, + "floors": 10.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 295, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 65.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.247, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 21450.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 4.2000000, + "floors": 17.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 296, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 50.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.970, + "impervious_roof_percent": 0.970, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 16500.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 1.000, + "total_far": 3.0000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 297, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 109.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 36036.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 3.0000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 298, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 57.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 18851.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 2.0000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 300, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 133.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.750, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 43986.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 3.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 301, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 76.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.425, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 25114.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 1.7000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 302, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 127.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.970, + "impervious_roof_percent": 0.970, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 42048.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 1.000, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 303, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 239.900, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 79176.40, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 4.5000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 305, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 71.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 23459.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 306, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 39.4944, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 307, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 32.9120, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 308, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 45.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 14850.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 1452.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 310, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 68.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 22681.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.450, + "household_density": 1.000, + "total_far": 1.2000000, + "floors": 3.000, + "residential_average_lot_size": 2219.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 311, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 43.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 14374.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 2000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 312, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 32.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.550, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 10643.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 1.1000000, + "floors": 2.000, + "residential_average_lot_size": 1742.40, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 313, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 37.500, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.233, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12372.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 0.7000000, + "floors": 3.000, + "residential_average_lot_size": 2519.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 315, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 38.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.375, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 12646.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 1.5000000, + "floors": 4.000, + "residential_average_lot_size": 2897.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 316, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 58.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 1.0542169, + "floors": 2.000, + "residential_average_lot_size": 1494.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 317, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 7465.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 1.1114458, + "floors": 4.000, + "residential_average_lot_size": 1660.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 318, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 56.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.400, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 1.1451613, + "floors": 3.000, + "residential_average_lot_size": 1550.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 320, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 34.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.571, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.450, + "household_density": 1.000, + "total_far": 0.6169789, + "floors": 2.000, + "residential_average_lot_size": 2509.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 321, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.595, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.7090643, + "floors": 2.000, + "residential_average_lot_size": 2736.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 322, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 58.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 1.0542169, + "floors": 2.000, + "residential_average_lot_size": 1494.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 323, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 34.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.544, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 0.5600000, + "floors": 2.000, + "residential_average_lot_size": 2500.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 1, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 63.800, + "combined_pop_emp_density": 33.4900, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 21054.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 4, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.000, + "combined_pop_emp_density": 14.8104, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 10, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.255, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.550, + "household_density": 1.000, + "total_far": 0.3333333, + "floors": 2.000, + "residential_average_lot_size": 15000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 15, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 78.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.433, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 25971.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 1.3000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 20, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 135.500, + "combined_pop_emp_density": 3.0076, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 44715.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.980, + "total_far": 15.2000000, + "floors": 35.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 26, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 191.600, + "combined_pop_emp_density": 2147.5080, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 63214.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 14.5000000, + "floors": 26.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 32, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 275.900, + "combined_pop_emp_density": 3.6543, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 91033.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.970, + "total_far": 16.6000000, + "floors": 20.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.425 + } + }, + { + "pk": 37, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 803.600, + "combined_pop_emp_density": 2.9100, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 265188.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.980, + "total_far": 13.2000000, + "floors": 31.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 44, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 39.600, + "combined_pop_emp_density": 33.6600, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 27.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 51, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 64.800, + "combined_pop_emp_density": 17.4744, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 60, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 33.800, + "combined_pop_emp_density": 37.0260, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 65, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 57.200, + "combined_pop_emp_density": 86.2488, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 18876.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 72, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 178.000, + "combined_pop_emp_density": 5.8091, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 58740.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.890, + "total_far": 3.8000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.225 + } + }, + { + "pk": 77, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 131.800, + "combined_pop_emp_density": 7.5872, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 43482.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.760, + "total_far": 1.2000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.900 + } + }, + { + "pk": 81, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 138.800, + "combined_pop_emp_density": 14.9156, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 45787.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.830, + "total_far": 5.7000000, + "floors": 7.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.075 + } + }, + { + "pk": 84, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 197.800, + "combined_pop_emp_density": 2.5934, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 65277.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.980, + "total_far": 4.6000000, + "floors": 7.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.450 + } + }, + { + "pk": 90, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 252.500, + "combined_pop_emp_density": 151.9294, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 83334.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.400, + "total_far": 4.8000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.000 + } + }, + { + "pk": 100, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.100, + "combined_pop_emp_density": 77.5783, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.1000000, + "floors": 2.000, + "residential_average_lot_size": 8196.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 105, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 61.200, + "combined_pop_emp_density": 8.5998, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.467, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 20190.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.700, + "total_far": 1.4000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.750 + } + }, + { + "pk": 111, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 25.1966, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.600, + "total_far": 2.0000000, + "floors": 2.000, + "residential_average_lot_size": 2395.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.500 + } + }, + { + "pk": 116, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 35.800, + "combined_pop_emp_density": 26.8126, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.520, + "total_far": 1.6000000, + "floors": 2.000, + "residential_average_lot_size": 8559.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.300 + } + }, + { + "pk": 122, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 129.800, + "combined_pop_emp_density": 20.4442, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 12846.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 339, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 58.165, + "combined_pop_emp_density": 8.9196, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.483, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.368, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1750000, + "floors": 1.050, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 324, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 58.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 1.0542169, + "floors": 2.000, + "residential_average_lot_size": 1494.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 325, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 10.900, + "combined_pop_emp_density": 98.7360, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.0000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 326, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.400, + "combined_pop_emp_density": 143.1672, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.9000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 327, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 6.500, + "combined_pop_emp_density": 44.4312, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.2000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 328, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.600, + "combined_pop_emp_density": 77.7546, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.1000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 329, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.600, + "combined_pop_emp_density": 103.6728, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.8000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 330, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 28.000, + "combined_pop_emp_density": 99.9702, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.7000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 331, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 10.000, + "combined_pop_emp_density": 85.1598, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 332, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 21.000, + "combined_pop_emp_density": 29.6208, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 333, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.600, + "combined_pop_emp_density": 37.0260, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 334, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 30.000, + "combined_pop_emp_density": 85.1598, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 2.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 335, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.000, + "combined_pop_emp_density": 3.0294, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.070, + "hardscape_percent": 0.931, + "impervious_roof_percent": 0.677, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.254, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.4500000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 336, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.200, + "combined_pop_emp_density": 21.2900, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.070, + "hardscape_percent": 0.931, + "impervious_roof_percent": 0.677, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.254, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.6900000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 337, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 29.000, + "combined_pop_emp_density": 13.2676, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.877, + "impervious_roof_percent": 0.434, + "softscape_and_landscape_percent": 0.123, + "impervious_hardscape_percent": 0.443, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.4300000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 803.68, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 338, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 8.000, + "combined_pop_emp_density": 20.9814, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.070, + "hardscape_percent": 0.931, + "impervious_roof_percent": 0.677, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.254, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.6800000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 340, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 10.740, + "combined_pop_emp_density": 28.8759, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.997, + "impervious_roof_percent": 0.997, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 0.536, + "total_far": 1.8500000, + "floors": 2.000, + "residential_average_lot_size": 7583.90, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.721 + } + }, + { + "pk": 342, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 1.9747, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 344, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 15.330, + "combined_pop_emp_density": 6.4395, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.800, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.200, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2800000, + "floors": 1.500, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 346, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 93.010, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.633, + "impervious_roof_percent": 0.633, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 27564.51, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.098, + "household_density": 1.000, + "total_far": 6.5600000, + "floors": 11.400, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 348, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 283.200, + "combined_pop_emp_density": 1748.5083, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.600, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 93457.04, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.440, + "household_density": 0.000, + "total_far": 12.5300000, + "floors": 28.700, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 351, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.660, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.448, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.570, + "household_density": 1.000, + "total_far": 0.5824899, + "floors": 2.000, + "residential_average_lot_size": 20535.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 353, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.500, + "combined_pop_emp_density": 5.9242, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 1815.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 355, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 142.040, + "combined_pop_emp_density": 639.5400, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.530, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.320, + "parking_structure_square_feet": 44263.56, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 9.5000000, + "floors": 32.200, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 357, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 186.298, + "combined_pop_emp_density": 6.9261, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.955, + "impervious_roof_percent": 0.955, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 61475.58, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.317, + "household_density": 0.870, + "total_far": 4.1180000, + "floors": 6.400, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.919 + } + }, + { + "pk": 359, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 914.480, + "combined_pop_emp_density": 2770.6575, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.520, + "impervious_roof_percent": 0.520, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 301780.38, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.350, + "household_density": 0.000, + "total_far": 20.2800000, + "floors": 65.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 361, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.340, + "combined_pop_emp_density": 43.3250, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 1.5550000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 363, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 88.465, + "combined_pop_emp_density": 2.5668, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.573, + "impervious_roof_percent": 0.573, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 29184.83, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.088, + "household_density": 0.956, + "total_far": 1.9400000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.289 + } + }, + { + "pk": 365, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.180, + "combined_pop_emp_density": 11.2594, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.560, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.290, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.1300000, + "floors": 2.700, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 368, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 174.725, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.489, + "impervious_roof_percent": 0.489, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 57665.86, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.143, + "household_density": 1.000, + "total_far": 3.4600000, + "floors": 4.950, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 370, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 59.170, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.210, + "impervious_roof_percent": 0.210, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 19521.48, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.182, + "household_density": 1.000, + "total_far": 0.9500000, + "floors": 3.000, + "residential_average_lot_size": 493.15, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 372, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 39.4944, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 374, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 48.550, + "combined_pop_emp_density": 15.4107, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.537, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.312, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2600000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 376, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 46.690, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.212, + "impervious_roof_percent": 0.212, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 15402.98, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.210, + "household_density": 1.000, + "total_far": 1.0750000, + "floors": 2.700, + "residential_average_lot_size": 2021.72, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 378, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 89.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.560, + "impervious_roof_percent": 0.560, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 23573.76, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 0.9600000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 380, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.100, + "combined_pop_emp_density": 4.4431, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 382, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.775, + "combined_pop_emp_density": 21.8223, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5100000, + "floors": 1.550, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 384, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 3.700, + "combined_pop_emp_density": 2.6928, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 386, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.009, + "combined_pop_emp_density": 0.0054, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.006, + "total_far": 0.0011900, + "floors": 1.000, + "residential_average_lot_size": 6221.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 389, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 142.360, + "combined_pop_emp_density": 16.2142, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.895, + "impervious_roof_percent": 0.895, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 46351.02, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 0.749, + "total_far": 2.5700000, + "floors": 3.900, + "residential_average_lot_size": 3217.20, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.409 + } + }, + { + "pk": 391, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 18.350, + "combined_pop_emp_density": 22.2156, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.250, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4500000, + "floors": 1.500, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 393, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 29.920, + "combined_pop_emp_density": 54.3581, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.641, + "impervious_roof_percent": 0.641, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.290, + "household_density": 0.000, + "total_far": 1.2000000, + "floors": 1.650, + "residential_average_lot_size": 5538.80, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 395, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.220, + "combined_pop_emp_density": 4.9146, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.056, + "hardscape_percent": 0.920, + "impervious_roof_percent": 0.628, + "softscape_and_landscape_percent": 0.025, + "impervious_hardscape_percent": 0.292, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 0.000, + "total_far": 0.5160000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 160.74, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 397, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 176.900, + "combined_pop_emp_density": 177.7248, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 58360.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 3.0000000, + "floors": 9.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 127, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 13146.41, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.496, + "impervious_roof_percent": 0.313, + "softscape_and_landscape_percent": 0.503, + "impervious_hardscape_percent": 0.183, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.600, + "household_density": 1.000, + "total_far": 0.2694949, + "floors": 2.000, + "residential_average_lot_size": 4950.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 134, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 92.600, + "combined_pop_emp_density": 560.8957, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.500, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 30546.50, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 5.0000000, + "floors": 10.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 140, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.100, + "combined_pop_emp_density": 189.6719, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.170, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 10263.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 0.000, + "total_far": 1.7000000, + "floors": 10.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 147, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 259.200, + "combined_pop_emp_density": 275.2862, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.598, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 81253.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 2.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 149, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 132.200, + "combined_pop_emp_density": 111.5717, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.951, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 13086.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 1.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 159, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 784.100, + "combined_pop_emp_density": 3.9204, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 4.5000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 161, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 396.000, + "combined_pop_emp_density": 8.6552, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.200, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 130680.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.500, + "total_far": 0.8000000, + "floors": 4.000, + "residential_average_lot_size": 5000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.250 + } + }, + { + "pk": 162, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 528.000, + "combined_pop_emp_density": 16.0604, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.320, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 174240.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.500, + "total_far": 1.6000000, + "floors": 5.000, + "residential_average_lot_size": 7117.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.250 + } + }, + { + "pk": 168, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 64.800, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 6414.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 176, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0293, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 182, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.100, + "combined_pop_emp_density": 0.0378, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.004, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.010, + "total_far": 0.0030000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.025 + } + }, + { + "pk": 187, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0542, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.020, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 7777.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.050 + } + }, + { + "pk": 195, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 0.0044, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.000, + "household_density": 0.000, + "total_far": 0.0010000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 202, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 2.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.042, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.120, + "household_density": 1.000, + "total_far": 0.0437500, + "floors": 2.000, + "residential_average_lot_size": 80000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 208, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.200, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.003, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.010, + "household_density": 1.000, + "total_far": 0.0035000, + "floors": 2.000, + "residential_average_lot_size": 1000000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 213, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.015, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 0.0187500, + "floors": 2.000, + "residential_average_lot_size": 160000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 219, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1855.000, + "combined_pop_emp_density": 4379.7848, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.950, + "impervious_roof_percent": 0.950, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 612150.90, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 33.4000000, + "floors": 52.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 226, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 181.000, + "combined_pop_emp_density": 2355.9513, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.150, + "impervious_roof_percent": 0.150, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 59730.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.250, + "household_density": 0.000, + "total_far": 19.5000000, + "floors": 80.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 231, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 181.000, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.980, + "impervious_roof_percent": 0.980, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 100558.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 24.9000000, + "floors": 58.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 237, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.569, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.8250000, + "floors": 2.000, + "residential_average_lot_size": 4000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 242, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 79.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.425, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 26161.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 1.7000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 244, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 138.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.750, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 45819.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 3.0000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 248, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 43.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.400, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 14374.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 2.000, + "residential_average_lot_size": 2000.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 253, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 57.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.375, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 18915.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.200, + "household_density": 1.000, + "total_far": 1.5000000, + "floors": 4.000, + "residential_average_lot_size": 2897.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 259, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 67.300, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.333, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 22215.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 1.0000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 264, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 77.400, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.844, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 7666.60, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 1.3000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 270, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.000, + "combined_pop_emp_density": 29.6208, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 276, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 24.6840, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 282, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 41.1400, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.5000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 287, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.500, + "combined_pop_emp_density": 5.9242, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 1815.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 293, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.900, + "impervious_roof_percent": 0.900, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 31.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.100, + "household_density": 1.000, + "total_far": 6.7000000, + "floors": 8.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 299, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 150.600, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.450, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 49708.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 2.7000000, + "floors": 6.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 304, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 88.900, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.650, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 29337.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 1.000, + "total_far": 2.6000000, + "floors": 4.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 309, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 48.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.300, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 15857.10, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.9000000, + "floors": 3.000, + "residential_average_lot_size": 1917.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 314, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 60.100, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.200, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 19827.30, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.150, + "household_density": 1.000, + "total_far": 0.8000000, + "floors": 4.000, + "residential_average_lot_size": 1450.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 319, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 33.700, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.850, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 33.70, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 1.000, + "total_far": 0.7910448, + "floors": 2.000, + "residential_average_lot_size": 1742.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 341, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 328.605, + "combined_pop_emp_density": 17.9131, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.607, + "impervious_roof_percent": 0.607, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 108447.57, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.075, + "household_density": 0.860, + "total_far": 13.3500000, + "floors": 38.700, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.961 + } + }, + { + "pk": 343, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 22.200, + "combined_pop_emp_density": 17.0889, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 345, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 4.600, + "combined_pop_emp_density": 1.8513, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.2500000, + "floors": 1.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 347, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.500, + "combined_pop_emp_density": 4.9368, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.450, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.400, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.4000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 349, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 13.590, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.263, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.639, + "household_density": 1.000, + "total_far": 0.3205412, + "floors": 2.000, + "residential_average_lot_size": 99243.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 350, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 16.120, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 2036.34, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.119, + "impervious_roof_percent": 0.064, + "softscape_and_landscape_percent": 0.081, + "impervious_hardscape_percent": 0.055, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.580, + "household_density": 1.000, + "total_far": 0.4312727, + "floors": 2.000, + "residential_average_lot_size": 21400.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 352, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 659.705, + "combined_pop_emp_density": 2.5694, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.481, + "impervious_roof_percent": 0.481, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 208630.08, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.237, + "household_density": 0.991, + "total_far": 17.0550000, + "floors": 68.550, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.456 + } + }, + { + "pk": 354, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 235.775, + "combined_pop_emp_density": 15.4447, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.982, + "impervious_roof_percent": 0.982, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 77803.27, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.450, + "household_density": 0.822, + "total_far": 8.1750000, + "floors": 13.750, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.749 + } + }, + { + "pk": 356, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 508.200, + "combined_pop_emp_density": 14.3246, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.305, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 167706.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.050, + "household_density": 0.500, + "total_far": 1.4800000, + "floors": 4.850, + "residential_average_lot_size": 6229.95, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.625 + } + }, + { + "pk": 358, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 169.120, + "combined_pop_emp_density": 235.4762, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.371, + "impervious_roof_percent": 0.371, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 55811.27, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.375, + "household_density": 0.080, + "total_far": 2.5850000, + "floors": 4.950, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.080 + } + }, + { + "pk": 360, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 57.740, + "combined_pop_emp_density": 510.4696, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.406, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 19050.26, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.280, + "household_density": 0.000, + "total_far": 3.9000000, + "floors": 9.600, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 362, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 61.310, + "combined_pop_emp_density": 17.6735, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.515, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.335, + "parking_structure_square_feet": 4836.20, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4800000, + "floors": 1.600, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 364, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 18.350, + "combined_pop_emp_density": 40.3920, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.250, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4500000, + "floors": 1.500, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 399, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 153.520, + "combined_pop_emp_density": 2.7369, + "residential_irrigated_square_feet": 18.41, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.343, + "impervious_roof_percent": 0.328, + "softscape_and_landscape_percent": 0.007, + "impervious_hardscape_percent": 0.015, + "parking_structure_square_feet": 49649.79, + "gross_net_ratio": 0.5536478, + "irrigated_percent": 0.136, + "household_density": 0.474, + "total_far": 4.4704578, + "floors": 11.895, + "residential_average_lot_size": 1316.63, + "commercial_irrigated_square_feet": 3.10, + "intersection_density": 0.0000, + "gross_population_density": 1.137 + } + }, + { + "pk": 402, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 47.460, + "combined_pop_emp_density": 1.4930, + "residential_irrigated_square_feet": 19.92, + "pervious_hardscape_percent": 0.004, + "hardscape_percent": 0.280, + "impervious_roof_percent": 0.265, + "softscape_and_landscape_percent": 0.007, + "impervious_hardscape_percent": 0.015, + "parking_structure_square_feet": 15296.89, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.138, + "household_density": 0.469, + "total_far": 1.4487550, + "floors": 3.073, + "residential_average_lot_size": 596.35, + "commercial_irrigated_square_feet": 3.85, + "intersection_density": 0.0000, + "gross_population_density": 1.118 + } + }, + { + "pk": 404, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 51.981, + "combined_pop_emp_density": 5.0012, + "residential_irrigated_square_feet": 21.44, + "pervious_hardscape_percent": 0.005, + "hardscape_percent": 0.349, + "impervious_roof_percent": 0.331, + "softscape_and_landscape_percent": 0.009, + "impervious_hardscape_percent": 0.018, + "parking_structure_square_feet": 15607.60, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.214, + "household_density": 0.244, + "total_far": 0.9682880, + "floors": 1.827, + "residential_average_lot_size": 1622.12, + "commercial_irrigated_square_feet": 27.85, + "intersection_density": 0.0000, + "gross_population_density": 0.442 + } + }, + { + "pk": 407, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 19.229, + "combined_pop_emp_density": 3.0751, + "residential_irrigated_square_feet": 41.18, + "pervious_hardscape_percent": 0.007, + "hardscape_percent": 0.320, + "impervious_roof_percent": 0.287, + "softscape_and_landscape_percent": 0.014, + "impervious_hardscape_percent": 0.033, + "parking_structure_square_feet": 2732.24, + "gross_net_ratio": 0.5701998, + "irrigated_percent": 0.277, + "household_density": 0.245, + "total_far": 0.6036295, + "floors": 1.296, + "residential_average_lot_size": 28921.60, + "commercial_irrigated_square_feet": 54.47, + "intersection_density": 0.0000, + "gross_population_density": 0.531 + } + }, + { + "pk": 409, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 21.153, + "combined_pop_emp_density": 3.1545, + "residential_irrigated_square_feet": 3.80, + "pervious_hardscape_percent": 0.011, + "hardscape_percent": 0.337, + "impervious_roof_percent": 0.261, + "softscape_and_landscape_percent": 0.023, + "impervious_hardscape_percent": 0.076, + "parking_structure_square_feet": 1365.59, + "gross_net_ratio": 0.4301998, + "irrigated_percent": 0.192, + "household_density": 0.020, + "total_far": 0.3838528, + "floors": 0.831, + "residential_average_lot_size": 1188.79, + "commercial_irrigated_square_feet": 78.81, + "intersection_density": 0.0000, + "gross_population_density": 0.014 + } + }, + { + "pk": 412, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 69.254, + "combined_pop_emp_density": 26.0294, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.007, + "hardscape_percent": 0.180, + "impervious_roof_percent": 0.149, + "softscape_and_landscape_percent": 0.012, + "impervious_hardscape_percent": 0.032, + "parking_structure_square_feet": 8594.37, + "gross_net_ratio": 0.6988970, + "irrigated_percent": 0.320, + "household_density": 0.000, + "total_far": 0.6921730, + "floors": 2.832, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 116.64, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 414, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 21.318, + "combined_pop_emp_density": 4.2630, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.026, + "hardscape_percent": 0.665, + "impervious_roof_percent": 0.489, + "softscape_and_landscape_percent": 0.027, + "impervious_hardscape_percent": 0.177, + "parking_structure_square_feet": 527.56, + "gross_net_ratio": 0.7564463, + "irrigated_percent": 0.247, + "household_density": 0.000, + "total_far": 0.4051072, + "floors": 1.051, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 220.08, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 416, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 6.049, + "combined_pop_emp_density": 0.5997, + "residential_irrigated_square_feet": 0.01, + "pervious_hardscape_percent": 0.005, + "hardscape_percent": 0.412, + "impervious_roof_percent": 0.320, + "softscape_and_landscape_percent": 0.002, + "impervious_hardscape_percent": 0.093, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.177, + "household_density": 0.001, + "total_far": 0.1402338, + "floors": 0.722, + "residential_average_lot_size": 905.24, + "commercial_irrigated_square_feet": 7.69, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 419, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 23.706, + "combined_pop_emp_density": 2.4584, + "residential_irrigated_square_feet": 329.60, + "pervious_hardscape_percent": 0.019, + "hardscape_percent": 0.391, + "impervious_roof_percent": 0.250, + "softscape_and_landscape_percent": 0.047, + "impervious_hardscape_percent": 0.141, + "parking_structure_square_feet": 898.12, + "gross_net_ratio": 0.7419603, + "irrigated_percent": 0.449, + "household_density": 0.361, + "total_far": 0.2830702, + "floors": 1.263, + "residential_average_lot_size": 190582.42, + "commercial_irrigated_square_feet": 348.32, + "intersection_density": 0.0000, + "gross_population_density": 0.902 + } + }, + { + "pk": 421, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 54.964, + "combined_pop_emp_density": 3.5389, + "residential_irrigated_square_feet": 28.63, + "pervious_hardscape_percent": 0.005, + "hardscape_percent": 0.426, + "impervious_roof_percent": 0.383, + "softscape_and_landscape_percent": 0.008, + "impervious_hardscape_percent": 0.043, + "parking_structure_square_feet": 13933.91, + "gross_net_ratio": 0.7641394, + "irrigated_percent": 0.166, + "household_density": 0.482, + "total_far": 0.9525226, + "floors": 2.395, + "residential_average_lot_size": 101.45, + "commercial_irrigated_square_feet": 16.80, + "intersection_density": 0.0000, + "gross_population_density": 1.135 + } + }, + { + "pk": 424, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 27.182, + "combined_pop_emp_density": 1.6401, + "residential_irrigated_square_feet": 122.65, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.186, + "impervious_roof_percent": 0.159, + "softscape_and_landscape_percent": 0.014, + "impervious_hardscape_percent": 0.026, + "parking_structure_square_feet": 5825.69, + "gross_net_ratio": 0.7094675, + "irrigated_percent": 0.312, + "household_density": 0.649, + "total_far": 0.4738560, + "floors": 1.662, + "residential_average_lot_size": 93917.39, + "commercial_irrigated_square_feet": 11.33, + "intersection_density": 0.0000, + "gross_population_density": 1.624 + } + }, + { + "pk": 426, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.243, + "combined_pop_emp_density": 1.8415, + "residential_irrigated_square_feet": 34.53, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.073, + "impervious_roof_percent": 0.059, + "softscape_and_landscape_percent": 0.004, + "impervious_hardscape_percent": 0.014, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.7756289, + "irrigated_percent": 0.269, + "household_density": 0.736, + "total_far": 0.1437078, + "floors": 1.555, + "residential_average_lot_size": 816411.02, + "commercial_irrigated_square_feet": 1.88, + "intersection_density": 0.0000, + "gross_population_density": 1.839 + } + }, + { + "pk": 429, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 2.705, + "combined_pop_emp_density": 0.2156, + "residential_irrigated_square_feet": 0.38, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.039, + "impervious_roof_percent": 0.022, + "softscape_and_landscape_percent": 0.005, + "impervious_hardscape_percent": 0.017, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.9374819, + "irrigated_percent": 0.024, + "household_density": 0.079, + "total_far": 0.0094390, + "floors": 1.015, + "residential_average_lot_size": 5482910.56, + "commercial_irrigated_square_feet": 0.22, + "intersection_density": 0.0000, + "gross_population_density": 0.188 + } + }, + { + "pk": 432, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.001, + "combined_pop_emp_density": 0.0000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.0915250, + "irrigated_percent": 0.000, + "household_density": 0.001, + "total_far": 0.0001089, + "floors": 0.092, + "residential_average_lot_size": 603.65, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 366, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.342, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.005, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.014, + "household_density": 1.000, + "total_far": 0.0057122, + "floors": 2.000, + "residential_average_lot_size": 5095631.40, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 367, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 639.940, + "combined_pop_emp_density": 3.5719, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.396, + "impervious_roof_percent": 0.396, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 130680.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 4.1000000, + "floors": 4.600, + "residential_average_lot_size": 7614.60, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 369, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.000, + "combined_pop_emp_density": 26.9280, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.600, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.250, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.3000000, + "floors": 2.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 371, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 54.6661, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.562, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.287, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.8250000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 373, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 31.800, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.595, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.750, + "household_density": 1.000, + "total_far": 0.7090643, + "floors": 2.000, + "residential_average_lot_size": 5472.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 375, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 58.740, + "combined_pop_emp_density": 43.0848, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.555, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.295, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.6400000, + "floors": 4.200, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 377, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 539.145, + "combined_pop_emp_density": 2.9146, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.677, + "impervious_roof_percent": 0.677, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 177913.26, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.245, + "household_density": 0.976, + "total_far": 11.8150000, + "floors": 30.700, + "residential_average_lot_size": 6534.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.385 + } + }, + { + "pk": 379, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.570, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.032, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.090, + "household_density": 1.000, + "total_far": 0.0395065, + "floors": 2.000, + "residential_average_lot_size": 455681.10, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 381, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 529.900, + "combined_pop_emp_density": 1631.4507, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.990, + "impervious_roof_percent": 0.990, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 174867.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.365, + "household_density": 0.436, + "total_far": 39.4900000, + "floors": 76.900, + "residential_average_lot_size": 24005.45, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.516 + } + }, + { + "pk": 383, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 19.7472, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.2000000, + "floors": 12.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 385, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 40.235, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.293, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 13269.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.105, + "household_density": 1.000, + "total_far": 0.7750000, + "floors": 2.650, + "residential_average_lot_size": 2162.15, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 387, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 111.600, + "combined_pop_emp_density": 91.4193, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.781, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 11046.59, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.460, + "household_density": 0.000, + "total_far": 0.8150000, + "floors": 4.300, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 388, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 80.820, + "combined_pop_emp_density": 22.4849, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.485, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.365, + "parking_structure_square_feet": 6736.46, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4600000, + "floors": 1.750, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 390, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.000, + "combined_pop_emp_density": 40.5523, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.570, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.280, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 0.4600000, + "floors": 8.400, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 2178.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 392, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 63.800, + "combined_pop_emp_density": 33.4900, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.750, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.100, + "parking_structure_square_feet": 21054.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.6000000, + "floors": 3.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 394, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 559.880, + "combined_pop_emp_density": 27.6539, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 1.000, + "impervious_roof_percent": 1.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 184753.80, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.500, + "household_density": 0.000, + "total_far": 4.6000000, + "floors": 5.100, + "residential_average_lot_size": 6417.50, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 396, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.160, + "combined_pop_emp_density": 2.5000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.352, + "impervious_roof_percent": 0.352, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.690, + "household_density": 1.000, + "total_far": 0.5330653, + "floors": 2.400, + "residential_average_lot_size": 37922.80, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.500 + } + }, + { + "pk": 398, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 260.102, + "combined_pop_emp_density": 26.8589, + "residential_irrigated_square_feet": 14.05, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.361, + "impervious_roof_percent": 0.351, + "softscape_and_landscape_percent": 0.005, + "impervious_hardscape_percent": 0.010, + "parking_structure_square_feet": 84684.20, + "gross_net_ratio": 0.5536478, + "irrigated_percent": 0.169, + "household_density": 0.379, + "total_far": 8.1344960, + "floors": 23.085, + "residential_average_lot_size": 2390.15, + "commercial_irrigated_square_feet": 6.45, + "intersection_density": 0.0000, + "gross_population_density": 0.837 + } + }, + { + "pk": 400, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 125.406, + "combined_pop_emp_density": 65.1471, + "residential_irrigated_square_feet": 1.90, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.265, + "impervious_roof_percent": 0.255, + "softscape_and_landscape_percent": 0.004, + "impervious_hardscape_percent": 0.010, + "parking_structure_square_feet": 39446.12, + "gross_net_ratio": 0.5536477, + "irrigated_percent": 0.199, + "household_density": 0.059, + "total_far": 3.5493744, + "floors": 8.337, + "residential_average_lot_size": 1078.71, + "commercial_irrigated_square_feet": 15.92, + "intersection_density": 0.0000, + "gross_population_density": 0.069 + } + }, + { + "pk": 401, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 79.275, + "combined_pop_emp_density": 11.7098, + "residential_irrigated_square_feet": 14.12, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.321, + "impervious_roof_percent": 0.308, + "softscape_and_landscape_percent": 0.007, + "impervious_hardscape_percent": 0.013, + "parking_structure_square_feet": 25741.74, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.186, + "household_density": 0.267, + "total_far": 1.9258753, + "floors": 3.848, + "residential_average_lot_size": 640.61, + "commercial_irrigated_square_feet": 15.52, + "intersection_density": 0.0000, + "gross_population_density": 0.526 + } + }, + { + "pk": 403, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 91.812, + "combined_pop_emp_density": 18.6219, + "residential_irrigated_square_feet": 0.67, + "pervious_hardscape_percent": 0.001, + "hardscape_percent": 0.318, + "impervious_roof_percent": 0.312, + "softscape_and_landscape_percent": 0.003, + "impervious_hardscape_percent": 0.006, + "parking_structure_square_feet": 25825.37, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.212, + "household_density": 0.028, + "total_far": 1.4699825, + "floors": 2.677, + "residential_average_lot_size": 1994.29, + "commercial_irrigated_square_feet": 13.04, + "intersection_density": 0.0000, + "gross_population_density": 0.012 + } + }, + { + "pk": 405, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 35.520, + "combined_pop_emp_density": 1.5969, + "residential_irrigated_square_feet": 109.87, + "pervious_hardscape_percent": 0.004, + "hardscape_percent": 0.259, + "impervious_roof_percent": 0.239, + "softscape_and_landscape_percent": 0.017, + "impervious_hardscape_percent": 0.020, + "parking_structure_square_feet": 9894.67, + "gross_net_ratio": 0.6443096, + "irrigated_percent": 0.282, + "household_density": 0.521, + "total_far": 0.7931751, + "floors": 1.776, + "residential_average_lot_size": 53059.50, + "commercial_irrigated_square_feet": 25.88, + "intersection_density": 0.0000, + "gross_population_density": 1.180 + } + }, + { + "pk": 406, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 55.551, + "combined_pop_emp_density": 11.2191, + "residential_irrigated_square_feet": 0.93, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.376, + "impervious_roof_percent": 0.367, + "softscape_and_landscape_percent": 0.004, + "impervious_hardscape_percent": 0.010, + "parking_structure_square_feet": 11610.83, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.194, + "household_density": 0.029, + "total_far": 0.9246575, + "floors": 1.353, + "residential_average_lot_size": 2840.89, + "commercial_irrigated_square_feet": 16.78, + "intersection_density": 0.0000, + "gross_population_density": 0.028 + } + }, + { + "pk": 408, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.519, + "combined_pop_emp_density": 1.5350, + "residential_irrigated_square_feet": 64.14, + "pervious_hardscape_percent": 0.004, + "hardscape_percent": 0.117, + "impervious_roof_percent": 0.103, + "softscape_and_landscape_percent": 0.008, + "impervious_hardscape_percent": 0.014, + "parking_structure_square_feet": 3774.60, + "gross_net_ratio": 0.6220770, + "irrigated_percent": 0.341, + "household_density": 0.540, + "total_far": 0.5040917, + "floors": 1.468, + "residential_average_lot_size": 30397.64, + "commercial_irrigated_square_feet": 9.81, + "intersection_density": 0.0000, + "gross_population_density": 1.344 + } + }, + { + "pk": 410, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 14.256, + "combined_pop_emp_density": 1.5133, + "residential_irrigated_square_feet": 254.63, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.084, + "impervious_roof_percent": 0.060, + "softscape_and_landscape_percent": 0.025, + "impervious_hardscape_percent": 0.025, + "parking_structure_square_feet": 601.75, + "gross_net_ratio": 0.6379912, + "irrigated_percent": 0.411, + "household_density": 0.575, + "total_far": 0.3765974, + "floors": 1.356, + "residential_average_lot_size": 57312.39, + "commercial_irrigated_square_feet": 27.74, + "intersection_density": 0.0000, + "gross_population_density": 1.433 + } + }, + { + "pk": 411, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 7.683, + "combined_pop_emp_density": 1.4774, + "residential_irrigated_square_feet": 68.71, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.083, + "impervious_roof_percent": 0.068, + "softscape_and_landscape_percent": 0.010, + "impervious_hardscape_percent": 0.015, + "parking_structure_square_feet": 601.75, + "gross_net_ratio": 0.6379912, + "irrigated_percent": 0.281, + "household_density": 0.575, + "total_far": 0.2087304, + "floors": 1.379, + "residential_average_lot_size": 777212.02, + "commercial_irrigated_square_feet": 7.49, + "intersection_density": 0.0000, + "gross_population_density": 1.433 + } + }, + { + "pk": 413, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 35.245, + "combined_pop_emp_density": 8.1690, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.027, + "hardscape_percent": 0.535, + "impervious_roof_percent": 0.370, + "softscape_and_landscape_percent": 0.046, + "impervious_hardscape_percent": 0.165, + "parking_structure_square_feet": 1757.57, + "gross_net_ratio": 0.6988970, + "irrigated_percent": 0.285, + "household_density": 0.000, + "total_far": 0.4994245, + "floors": 1.596, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 396.72, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 415, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.734, + "combined_pop_emp_density": 3.2713, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.019, + "hardscape_percent": 0.705, + "impervious_roof_percent": 0.531, + "softscape_and_landscape_percent": 0.021, + "impervious_hardscape_percent": 0.173, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.7453567, + "irrigated_percent": 0.289, + "household_density": 0.000, + "total_far": 0.3186176, + "floors": 0.968, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 201.21, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 417, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 83.382, + "combined_pop_emp_density": 6.8083, + "residential_irrigated_square_feet": 9.10, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.352, + "impervious_roof_percent": 0.341, + "softscape_and_landscape_percent": 0.003, + "impervious_hardscape_percent": 0.011, + "parking_structure_square_feet": 25999.89, + "gross_net_ratio": 0.6925699, + "irrigated_percent": 0.154, + "household_density": 0.392, + "total_far": 1.5684979, + "floors": 3.432, + "residential_average_lot_size": 1467.85, + "commercial_irrigated_square_feet": 6.97, + "intersection_density": 0.0000, + "gross_population_density": 0.850 + } + }, + { + "pk": 418, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 38.269, + "combined_pop_emp_density": 9.4124, + "residential_irrigated_square_feet": 50.75, + "pervious_hardscape_percent": 0.023, + "hardscape_percent": 0.530, + "impervious_roof_percent": 0.371, + "softscape_and_landscape_percent": 0.045, + "impervious_hardscape_percent": 0.159, + "parking_structure_square_feet": 2607.96, + "gross_net_ratio": 0.6925699, + "irrigated_percent": 0.295, + "household_density": 0.088, + "total_far": 0.4501705, + "floors": 1.212, + "residential_average_lot_size": 1070.02, + "commercial_irrigated_square_feet": 349.52, + "intersection_density": 0.0000, + "gross_population_density": 0.187 + } + }, + { + "pk": 420, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 60.013, + "combined_pop_emp_density": 7.7329, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.027, + "hardscape_percent": 0.455, + "impervious_roof_percent": 0.266, + "softscape_and_landscape_percent": 0.053, + "impervious_hardscape_percent": 0.189, + "parking_structure_square_feet": 4413.14, + "gross_net_ratio": 0.7641394, + "irrigated_percent": 0.373, + "household_density": 0.000, + "total_far": 0.3830248, + "floors": 1.754, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 663.93, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 422, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 43.031, + "combined_pop_emp_density": 3.7744, + "residential_irrigated_square_feet": 14.78, + "pervious_hardscape_percent": 0.004, + "hardscape_percent": 0.411, + "impervious_roof_percent": 0.371, + "softscape_and_landscape_percent": 0.005, + "impervious_hardscape_percent": 0.040, + "parking_structure_square_feet": 8117.82, + "gross_net_ratio": 0.7641394, + "irrigated_percent": 0.207, + "household_density": 0.344, + "total_far": 0.6289631, + "floors": 1.960, + "residential_average_lot_size": 235.95, + "commercial_irrigated_square_feet": 18.06, + "intersection_density": 0.0000, + "gross_population_density": 0.860 + } + }, + { + "pk": 423, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 58.122, + "combined_pop_emp_density": 1.7024, + "residential_irrigated_square_feet": 8.63, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.378, + "impervious_roof_percent": 0.367, + "softscape_and_landscape_percent": 0.003, + "impervious_hardscape_percent": 0.010, + "parking_structure_square_feet": 15595.54, + "gross_net_ratio": 0.7094675, + "irrigated_percent": 0.097, + "household_density": 0.679, + "total_far": 0.6588908, + "floors": 2.063, + "residential_average_lot_size": 86.86, + "commercial_irrigated_square_feet": 0.39, + "intersection_density": 0.0000, + "gross_population_density": 1.697 + } + }, + { + "pk": 425, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.289, + "combined_pop_emp_density": 1.6350, + "residential_irrigated_square_feet": 283.56, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.154, + "impervious_roof_percent": 0.122, + "softscape_and_landscape_percent": 0.022, + "impervious_hardscape_percent": 0.032, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.7094675, + "irrigated_percent": 0.460, + "household_density": 0.649, + "total_far": 0.3250911, + "floors": 1.450, + "residential_average_lot_size": 204227.71, + "commercial_irrigated_square_feet": 26.20, + "intersection_density": 0.0000, + "gross_population_density": 1.624 + } + }, + { + "pk": 427, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.276, + "combined_pop_emp_density": 2.1241, + "residential_irrigated_square_feet": 0.29, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.001, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.8508578, + "irrigated_percent": 0.065, + "household_density": 0.850, + "total_far": 0.0299787, + "floors": 1.701, + "residential_average_lot_size": 8190890.13, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.124 + } + }, + { + "pk": 428, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.372, + "combined_pop_emp_density": 1.8536, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.000, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.9260494, + "irrigated_percent": 0.017, + "household_density": 0.742, + "total_far": 0.0077248, + "floors": 1.667, + "residential_average_lot_size": 8897860.18, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.852 + } + }, + { + "pk": 430, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 74.134, + "combined_pop_emp_density": 13.5995, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.031, + "hardscape_percent": 0.555, + "impervious_roof_percent": 0.418, + "softscape_and_landscape_percent": 0.062, + "impervious_hardscape_percent": 0.137, + "parking_structure_square_feet": 16631.01, + "gross_net_ratio": 0.6889867, + "irrigated_percent": 0.593, + "household_density": 0.000, + "total_far": 0.6170000, + "floors": 2.002, + "residential_average_lot_size": 533.02, + "commercial_irrigated_square_feet": 1105.05, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 431, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 100.958, + "combined_pop_emp_density": 39.1683, + "residential_irrigated_square_feet": 31.79, + "pervious_hardscape_percent": 0.022, + "hardscape_percent": 0.443, + "impervious_roof_percent": 0.388, + "softscape_and_landscape_percent": 0.043, + "impervious_hardscape_percent": 0.055, + "parking_structure_square_feet": 30745.20, + "gross_net_ratio": 0.6247052, + "irrigated_percent": 0.437, + "household_density": 0.039, + "total_far": 1.5578942, + "floors": 4.269, + "residential_average_lot_size": 21412.07, + "commercial_irrigated_square_feet": 446.56, + "intersection_density": 0.0000, + "gross_population_density": 0.085 + } + }, + { + "pk": 1, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8886240000, + "net_built_up_area": 666.468, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0180000000, + "building_use_definition": 7, + "gross_built_up_area": 784.080, + "built_form": 1 + } + }, + { + "pk": 2, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 35.1747000000, + "net_built_up_area": 21104.820, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5700000000, + "building_use_definition": 8, + "gross_built_up_area": 24829.200, + "built_form": 1 + } + }, + { + "pk": 3, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5924160000, + "net_built_up_area": 444.312, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0120000000, + "building_use_definition": 9, + "gross_built_up_area": 522.720, + "built_form": 1 + } + }, + { + "pk": 4, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.0367280000, + "net_built_up_area": 777.546, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0210000000, + "building_use_definition": 7, + "gross_built_up_area": 914.760, + "built_form": 2 + } + }, + { + "pk": 5, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 41.0371500000, + "net_built_up_area": 24622.290, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6650000000, + "building_use_definition": 8, + "gross_built_up_area": 28967.400, + "built_form": 2 + } + }, + { + "pk": 6, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6911520000, + "net_built_up_area": 518.364, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0140000000, + "building_use_definition": 9, + "gross_built_up_area": 609.840, + "built_form": 2 + } + }, + { + "pk": 7, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.0889230769, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 650.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 8, + "gross_built_up_area": 13068.000, + "built_form": 3 + } + }, + { + "pk": 8, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.8104000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 10, + "gross_built_up_area": 13068.000, + "built_form": 4 + } + }, + { + "pk": 9, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.6208000000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 10, + "gross_built_up_area": 26136.000, + "built_form": 5 + } + }, + { + "pk": 10, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 10, + "gross_built_up_area": 21780.000, + "built_form": 6 + } + }, + { + "pk": 11, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.3680000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 10, + "gross_built_up_area": 43560.000, + "built_form": 7 + } + }, + { + "pk": 12, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.3560000000, + "net_built_up_area": 12196.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2800.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2800000000, + "building_use_definition": 11, + "gross_built_up_area": 12196.800, + "built_form": 8 + } + }, + { + "pk": 13, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1780000000, + "net_built_up_area": 6534.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1500000000, + "building_use_definition": 11, + "gross_built_up_area": 6534.000, + "built_form": 9 + } + }, + { + "pk": 14, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9040000000, + "net_built_up_area": 14519.999, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3333333000, + "building_use_definition": 11, + "gross_built_up_area": 14519.999, + "built_form": 10 + } + }, + { + "pk": 15, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1780000000, + "net_built_up_area": 10890.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2500000000, + "building_use_definition": 11, + "gross_built_up_area": 10890.000, + "built_form": 11 + } + }, + { + "pk": 16, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4444444444, + "net_built_up_area": 14284.443, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3214.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3279257000, + "building_use_definition": 11, + "gross_built_up_area": 14284.443, + "built_form": 12 + } + }, + { + "pk": 17, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.1876562200, + "net_built_up_area": 20754.023, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4956.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4764468000, + "building_use_definition": 11, + "gross_built_up_area": 20754.023, + "built_form": 13 + } + }, + { + "pk": 18, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.9868073879, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1516.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 12, + "gross_built_up_area": 34848.000, + "built_form": 14 + } + }, + { + "pk": 19, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.1278227524, + "net_built_up_area": 56628.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2347.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.3000000000, + "building_use_definition": 12, + "gross_built_up_area": 56628.000, + "built_form": 15 + } + }, + { + "pk": 20, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 30.6760563380, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1136.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 12, + "gross_built_up_area": 34848.000, + "built_form": 16 + } + }, + { + "pk": 21, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.4339957895, + "net_built_up_area": 7331.148, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1980000000, + "building_use_definition": 7, + "gross_built_up_area": 8624.880, + "built_form": 17 + } + }, + { + "pk": 22, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.2893305263, + "net_built_up_area": 4887.432, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1320000000, + "building_use_definition": 13, + "gross_built_up_area": 5749.920, + "built_form": 17 + } + }, + { + "pk": 23, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 109.0725797392, + "net_built_up_area": 217490.724, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1994.000, + "percent": 0.8900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.8740000000, + "building_use_definition": 14, + "gross_built_up_area": 255871.440, + "built_form": 17 + } + }, + { + "pk": 24, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 58.6491840000, + "net_built_up_area": 14662.296, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3960000000, + "building_use_definition": 15, + "gross_built_up_area": 17249.760, + "built_form": 17 + } + }, + { + "pk": 25, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.9550652632, + "net_built_up_area": 9478.656, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2560000000, + "building_use_definition": 7, + "gross_built_up_area": 11151.360, + "built_form": 18 + } + }, + { + "pk": 26, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 353.5163076923, + "net_built_up_area": 303316.992, + "vacancy_rate": 0.000, + "square_feet_per_unit": 858.000, + "percent": 0.6400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.1920000000, + "building_use_definition": 14, + "gross_built_up_area": 356843.520, + "built_form": 18 + } + }, + { + "pk": 27, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1074.2476800000, + "net_built_up_area": 161137.152, + "vacancy_rate": 0.000, + "square_feet_per_unit": 150.000, + "percent": 0.3400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.3520000000, + "building_use_definition": 15, + "gross_built_up_area": 189573.120, + "built_form": 18 + } + }, + { + "pk": 28, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.0309052632, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 7, + "gross_built_up_area": 7840.800, + "built_form": 19 + } + }, + { + "pk": 29, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 114.9443085607, + "net_built_up_area": 122185.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1063.000, + "percent": 0.5500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.3000000000, + "building_use_definition": 14, + "gross_built_up_area": 143748.000, + "built_form": 19 + } + }, + { + "pk": 30, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 373.2220800000, + "net_built_up_area": 93305.520, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.4200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.5200000000, + "building_use_definition": 15, + "gross_built_up_area": 109771.200, + "built_form": 19 + } + }, + { + "pk": 31, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 27.8784000000, + "net_built_up_area": 13242.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3040000000, + "building_use_definition": 13, + "gross_built_up_area": 13242.240, + "built_form": 20 + } + }, + { + "pk": 32, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 401.3627381443, + "net_built_up_area": 467186.227, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1164.000, + "percent": 0.9800000, + "efficiency": 0.7200, + "household_size": 2.500, + "floor_area_ratio": 14.8960000000, + "building_use_definition": 14, + "gross_built_up_area": 648869.760, + "built_form": 20 + } + }, + { + "pk": 33, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 60.8005894737, + "net_built_up_area": 28880.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7800000000, + "building_use_definition": 7, + "gross_built_up_area": 33976.800, + "built_form": 21 + } + }, + { + "pk": 34, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 36.4803536842, + "net_built_up_area": 17328.168, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4680000000, + "building_use_definition": 13, + "gross_built_up_area": 20386.080, + "built_form": 21 + } + }, + { + "pk": 35, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 350.5258258575, + "net_built_up_area": 531397.152, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1516.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 14.3520000000, + "building_use_definition": 14, + "gross_built_up_area": 625173.120, + "built_form": 21 + } + }, + { + "pk": 36, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 36.7921515789, + "net_built_up_area": 17476.272, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4720000000, + "building_use_definition": 7, + "gross_built_up_area": 20560.320, + "built_form": 22 + } + }, + { + "pk": 37, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.3960757895, + "net_built_up_area": 8738.136, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2360000000, + "building_use_definition": 13, + "gross_built_up_area": 10280.160, + "built_form": 22 + } + }, + { + "pk": 38, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 404.2247952756, + "net_built_up_area": 410692.392, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1016.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 11.0920000000, + "building_use_definition": 14, + "gross_built_up_area": 483167.520, + "built_form": 22 + } + }, + { + "pk": 39, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 33.1285263158, + "net_built_up_area": 15736.050, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4250000000, + "building_use_definition": 7, + "gross_built_up_area": 18513.000, + "built_form": 23 + } + }, + { + "pk": 40, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.8771157895, + "net_built_up_area": 9441.630, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2550000000, + "building_use_definition": 13, + "gross_built_up_area": 11107.800, + "built_form": 23 + } + }, + { + "pk": 41, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 221.0254351145, + "net_built_up_area": 289543.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1310.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 7.8200000000, + "building_use_definition": 14, + "gross_built_up_area": 340639.200, + "built_form": 23 + } + }, + { + "pk": 42, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 54.1252800000, + "net_built_up_area": 29768.904, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8040000000, + "building_use_definition": 7, + "gross_built_up_area": 35022.240, + "built_form": 24 + } + }, + { + "pk": 43, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 160.5186000000, + "net_built_up_area": 163728.972, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1020.000, + "percent": 0.2200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.4220000000, + "building_use_definition": 14, + "gross_built_up_area": 192622.320, + "built_form": 24 + } + }, + { + "pk": 44, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2202.8988960000, + "net_built_up_area": 550724.724, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.7400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 14.8740000000, + "building_use_definition": 15, + "gross_built_up_area": 647911.440, + "built_form": 24 + } + }, + { + "pk": 45, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.2185800000, + "net_built_up_area": 6109.290, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1650000000, + "building_use_definition": 9, + "gross_built_up_area": 7187.400, + "built_form": 25 + } + }, + { + "pk": 46, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2419.2788400000, + "net_built_up_area": 604819.710, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 16.3350000000, + "building_use_definition": 15, + "gross_built_up_area": 711552.600, + "built_form": 25 + } + }, + { + "pk": 47, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2147.5080000000, + "net_built_up_area": 536877.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 14.5000000000, + "building_use_definition": 15, + "gross_built_up_area": 631620.000, + "built_form": 26 + } + }, + { + "pk": 48, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.5533040000, + "net_built_up_area": 3776.652, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1020000000, + "building_use_definition": 7, + "gross_built_up_area": 4443.120, + "built_form": 27 + } + }, + { + "pk": 49, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.0355360000, + "net_built_up_area": 2517.768, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0680000000, + "building_use_definition": 9, + "gross_built_up_area": 2962.080, + "built_form": 27 + } + }, + { + "pk": 50, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 478.3759200000, + "net_built_up_area": 119593.980, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.2300000000, + "building_use_definition": 15, + "gross_built_up_area": 140698.800, + "built_form": 27 + } + }, + { + "pk": 51, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.7757620818, + "net_built_up_area": 13329.360, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3600000000, + "building_use_definition": 7, + "gross_built_up_area": 15681.600, + "built_form": 28 + } + }, + { + "pk": 52, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.3878810409, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 9, + "gross_built_up_area": 7840.800, + "built_form": 28 + } + }, + { + "pk": 53, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1252.9598400000, + "net_built_up_area": 313239.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.4600000000, + "building_use_definition": 15, + "gross_built_up_area": 368517.600, + "built_form": 28 + } + }, + { + "pk": 54, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.6464684015, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 7, + "gross_built_up_area": 13068.000, + "built_form": 29 + } + }, + { + "pk": 55, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.3878810409, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 9, + "gross_built_up_area": 7840.800, + "built_form": 29 + } + }, + { + "pk": 56, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 817.5340800000, + "net_built_up_area": 204383.520, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.5200000000, + "building_use_definition": 15, + "gross_built_up_area": 240451.200, + "built_form": 29 + } + }, + { + "pk": 57, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 32.5828800000, + "net_built_up_area": 16291.440, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4400000000, + "building_use_definition": 7, + "gross_built_up_area": 19166.400, + "built_form": 30 + } + }, + { + "pk": 58, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.4371600000, + "net_built_up_area": 12218.580, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3300000000, + "building_use_definition": 13, + "gross_built_up_area": 14374.800, + "built_form": 30 + } + }, + { + "pk": 59, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.4371600000, + "net_built_up_area": 12218.580, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3300000000, + "building_use_definition": 9, + "gross_built_up_area": 14374.800, + "built_form": 30 + } + }, + { + "pk": 60, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1621.4774400000, + "net_built_up_area": 405369.360, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9000000, + "efficiency": 0.9400, + "household_size": 2.500, + "floor_area_ratio": 9.9000000000, + "building_use_definition": 15, + "gross_built_up_area": 431244.000, + "built_form": 30 + } + }, + { + "pk": 61, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.0698800000, + "net_built_up_area": 7034.940, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1900000000, + "building_use_definition": 7, + "gross_built_up_area": 8276.400, + "built_form": 31 + } + }, + { + "pk": 62, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2785.8362400000, + "net_built_up_area": 696459.060, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 18.8100000000, + "building_use_definition": 15, + "gross_built_up_area": 819363.600, + "built_form": 31 + } + }, + { + "pk": 63, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.9754400000, + "net_built_up_area": 18438.948, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4980000000, + "building_use_definition": 13, + "gross_built_up_area": 21692.880, + "built_form": 32 + } + }, + { + "pk": 64, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 569.9738546845, + "net_built_up_area": 596192.652, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1046.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 16.1020000000, + "building_use_definition": 14, + "gross_built_up_area": 701403.120, + "built_form": 32 + } + }, + { + "pk": 65, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.2185800000, + "net_built_up_area": 6109.290, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1650000000, + "building_use_definition": 13, + "gross_built_up_area": 7187.400, + "built_form": 33 + } + }, + { + "pk": 66, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 208.6251750000, + "net_built_up_area": 183590.154, + "vacancy_rate": 0.000, + "square_feet_per_unit": 880.000, + "percent": 0.9700000, + "efficiency": 0.7900, + "household_size": 2.500, + "floor_area_ratio": 5.3350000000, + "building_use_definition": 14, + "gross_built_up_area": 232392.600, + "built_form": 33 + } + }, + { + "pk": 67, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.0490000000, + "net_built_up_area": 2776.950, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750000000, + "building_use_definition": 13, + "gross_built_up_area": 3267.000, + "built_form": 34 + } + }, + { + "pk": 68, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 232.1942989865, + "net_built_up_area": 274918.050, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 7.4250000000, + "building_use_definition": 14, + "gross_built_up_area": 323433.000, + "built_form": 34 + } + }, + { + "pk": 69, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.8862400000, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 13, + "gross_built_up_area": 7840.800, + "built_form": 35 + } + }, + { + "pk": 70, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 215.4913200000, + "net_built_up_area": 215491.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.8200000000, + "building_use_definition": 14, + "gross_built_up_area": 253519.200, + "built_form": 35 + } + }, + { + "pk": 71, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.3539368421, + "net_built_up_area": 4443.120, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1200000000, + "building_use_definition": 7, + "gross_built_up_area": 5227.200, + "built_form": 36 + } + }, + { + "pk": 72, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.7078736842, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 13, + "gross_built_up_area": 10454.400, + "built_form": 36 + } + }, + { + "pk": 73, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 165.9989189189, + "net_built_up_area": 196542.720, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 0.9400000, + "efficiency": 0.8000, + "household_size": 2.500, + "floor_area_ratio": 5.6400000000, + "building_use_definition": 14, + "gross_built_up_area": 245678.400, + "built_form": 36 + } + }, + { + "pk": 74, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.9996800000, + "net_built_up_area": 11499.840, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2640000000, + "building_use_definition": 13, + "gross_built_up_area": 11499.840, + "built_form": 37 + } + }, + { + "pk": 75, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 552.4432941176, + "net_built_up_area": 507142.944, + "vacancy_rate": 0.000, + "square_feet_per_unit": 918.000, + "percent": 0.9800000, + "efficiency": 0.9000, + "household_size": 2.500, + "floor_area_ratio": 12.9360000000, + "building_use_definition": 14, + "gross_built_up_area": 563492.160, + "built_form": 37 + } + }, + { + "pk": 76, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.9550652632, + "net_built_up_area": 9478.656, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2560000000, + "building_use_definition": 13, + "gross_built_up_area": 11151.360, + "built_form": 38 + } + }, + { + "pk": 77, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 396.9693538462, + "net_built_up_area": 464454.144, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1170.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 12.5440000000, + "building_use_definition": 14, + "gross_built_up_area": 546416.640, + "built_form": 38 + } + }, + { + "pk": 78, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.7913200000, + "net_built_up_area": 4406.094, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1190000000, + "building_use_definition": 13, + "gross_built_up_area": 5183.640, + "built_form": 39 + } + }, + { + "pk": 79, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 351.7768596774, + "net_built_up_area": 436203.306, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1240.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 11.7810000000, + "building_use_definition": 14, + "gross_built_up_area": 513180.360, + "built_form": 39 + } + }, + { + "pk": 80, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 177.7248000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 16, + "gross_built_up_area": 130680.000, + "built_form": 40 + } + }, + { + "pk": 81, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 118.4832000000, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 16, + "gross_built_up_area": 87120.000, + "built_form": 41 + } + }, + { + "pk": 82, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 414.4219200000, + "net_built_up_area": 227932.056, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.3600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 6.1560000000, + "building_use_definition": 7, + "gross_built_up_area": 268155.360, + "built_form": 42 + } + }, + { + "pk": 83, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 736.7500800000, + "net_built_up_area": 405212.544, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.6400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 10.9440000000, + "building_use_definition": 17, + "gross_built_up_area": 476720.640, + "built_form": 42 + } + }, + { + "pk": 84, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 68.9356800000, + "net_built_up_area": 37914.624, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.1600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0240000000, + "building_use_definition": 7, + "gross_built_up_area": 44605.440, + "built_form": 43 + } + }, + { + "pk": 85, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 361.9123200000, + "net_built_up_area": 199051.776, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.8400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.3760000000, + "building_use_definition": 17, + "gross_built_up_area": 234178.560, + "built_form": 43 + } + }, + { + "pk": 86, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.3660000000, + "net_built_up_area": 1851.300, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0500000000, + "building_use_definition": 7, + "gross_built_up_area": 2178.000, + "built_form": 44 + } + }, + { + "pk": 87, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 30.2940000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 17, + "gross_built_up_area": 19602.000, + "built_form": 44 + } + }, + { + "pk": 88, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.3660000000, + "net_built_up_area": 1851.300, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0500000000, + "building_use_definition": 7, + "gross_built_up_area": 2178.000, + "built_form": 45 + } + }, + { + "pk": 89, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 63.9540000000, + "net_built_up_area": 35174.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9500000000, + "building_use_definition": 17, + "gross_built_up_area": 41382.000, + "built_form": 45 + } + }, + { + "pk": 90, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6732000000, + "net_built_up_area": 370.260, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0100000000, + "building_use_definition": 7, + "gross_built_up_area": 435.600, + "built_form": 46 + } + }, + { + "pk": 91, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.7908000000, + "net_built_up_area": 7034.940, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1900000000, + "building_use_definition": 17, + "gross_built_up_area": 8276.400, + "built_form": 46 + } + }, + { + "pk": 92, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.3562000000, + "net_built_up_area": 1295.910, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0350000000, + "building_use_definition": 7, + "gross_built_up_area": 1524.600, + "built_form": 47 + } + }, + { + "pk": 93, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 44.7678000000, + "net_built_up_area": 24622.290, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6650000000, + "building_use_definition": 17, + "gross_built_up_area": 28967.400, + "built_form": 47 + } + }, + { + "pk": 94, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 16.1268800000, + "net_built_up_area": 14514.192, + "vacancy_rate": 0.000, + "square_feet_per_unit": 900.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3920000000, + "building_use_definition": 18, + "gross_built_up_area": 17075.520, + "built_form": 48 + } + }, + { + "pk": 95, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5924160000, + "net_built_up_area": 296.208, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0080000000, + "building_use_definition": 15, + "gross_built_up_area": 348.480, + "built_form": 48 + } + }, + { + "pk": 96, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.9012142857, + "net_built_up_area": 8330.850, + "vacancy_rate": 0.000, + "square_feet_per_unit": 700.000, + "percent": 0.7500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2250000000, + "building_use_definition": 18, + "gross_built_up_area": 9801.000, + "built_form": 49 + } + }, + { + "pk": 97, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.2565000000, + "net_built_up_area": 2776.950, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750000000, + "building_use_definition": 15, + "gross_built_up_area": 3267.000, + "built_form": 49 + } + }, + { + "pk": 98, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.1412500000, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 800.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 18, + "gross_built_up_area": 21780.000, + "built_form": 50 + } + }, + { + "pk": 99, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.9576100000, + "net_built_up_area": 17957.610, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4850000000, + "building_use_definition": 18, + "gross_built_up_area": 21126.600, + "built_form": 51 + } + }, + { + "pk": 100, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8513000000, + "net_built_up_area": 555.390, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0150000000, + "building_use_definition": 15, + "gross_built_up_area": 653.400, + "built_form": 51 + } + }, + { + "pk": 101, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 16.6617000000, + "net_built_up_area": 13329.360, + "vacancy_rate": 0.000, + "square_feet_per_unit": 800.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3600000000, + "building_use_definition": 19, + "gross_built_up_area": 15681.600, + "built_form": 52 + } + }, + { + "pk": 102, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.1078000000, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 800.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 20, + "gross_built_up_area": 10454.400, + "built_form": 52 + } + }, + { + "pk": 103, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.9841875000, + "net_built_up_area": 17587.350, + "vacancy_rate": 0.000, + "square_feet_per_unit": 800.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4750000000, + "building_use_definition": 20, + "gross_built_up_area": 20691.000, + "built_form": 53 + } + }, + { + "pk": 104, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6447142857, + "net_built_up_area": 925.650, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0250000000, + "building_use_definition": 15, + "gross_built_up_area": 1089.000, + "built_form": 53 + } + }, + { + "pk": 105, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.2191760000, + "net_built_up_area": 10219.176, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2760000000, + "building_use_definition": 20, + "gross_built_up_area": 12022.560, + "built_form": 54 + } + }, + { + "pk": 106, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.5389257143, + "net_built_up_area": 888.624, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0240000000, + "building_use_definition": 15, + "gross_built_up_area": 1045.440, + "built_form": 54 + } + }, + { + "pk": 107, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.7360500000, + "net_built_up_area": 15736.050, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.8500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4250000000, + "building_use_definition": 20, + "gross_built_up_area": 18513.000, + "built_form": 55 + } + }, + { + "pk": 108, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9341428571, + "net_built_up_area": 2776.950, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.1500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750000000, + "building_use_definition": 15, + "gross_built_up_area": 3267.000, + "built_form": 55 + } + }, + { + "pk": 109, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.2565000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 18, + "gross_built_up_area": 13068.000, + "built_form": 56 + } + }, + { + "pk": 110, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.2280000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 900.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 18, + "gross_built_up_area": 8712.000, + "built_form": 57 + } + }, + { + "pk": 111, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.0042162162, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1850.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 18, + "gross_built_up_area": 13068.000, + "built_form": 58 + } + }, + { + "pk": 112, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4936800000, + "net_built_up_area": 370.260, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0100000000, + "building_use_definition": 7, + "gross_built_up_area": 435.600, + "built_form": 59 + } + }, + { + "pk": 113, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.1189500000, + "net_built_up_area": 18142.740, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4900000000, + "building_use_definition": 18, + "gross_built_up_area": 21344.400, + "built_form": 59 + } + }, + { + "pk": 114, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 37.0260000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 400.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 15, + "gross_built_up_area": 17424.000, + "built_form": 60 + } + }, + { + "pk": 115, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.8080000000, + "net_built_up_area": 16262.399, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2800.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3733333000, + "building_use_definition": 11, + "gross_built_up_area": 16262.399, + "built_form": 61 + } + }, + { + "pk": 116, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.8080000000, + "net_built_up_area": 23231.999, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5333333000, + "building_use_definition": 11, + "gross_built_up_area": 23231.999, + "built_form": 62 + } + }, + { + "pk": 117, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.5831837990, + "net_built_up_area": 26687.618, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4780.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.6126634000, + "building_use_definition": 11, + "gross_built_up_area": 26687.618, + "built_form": 63 + } + }, + { + "pk": 118, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.8080000000, + "net_built_up_area": 23231.999, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5333333000, + "building_use_definition": 11, + "gross_built_up_area": 23231.999, + "built_form": 64 + } + }, + { + "pk": 119, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 86.2488000000, + "net_built_up_area": 43124.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 15, + "gross_built_up_area": 43560.000, + "built_form": 65 + } + }, + { + "pk": 120, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.8080000000, + "net_built_up_area": 4356.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 7, + "gross_built_up_area": 4356.000, + "built_form": 66 + } + }, + { + "pk": 121, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4810400000, + "net_built_up_area": 1110.780, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0300000000, + "building_use_definition": 7, + "gross_built_up_area": 1306.800, + "built_form": 67 + } + }, + { + "pk": 122, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9873600000, + "net_built_up_area": 740.520, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0200000000, + "building_use_definition": 10, + "gross_built_up_area": 871.200, + "built_form": 67 + } + }, + { + "pk": 123, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.4052000000, + "net_built_up_area": 5553.900, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.7500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1500000000, + "building_use_definition": 9, + "gross_built_up_area": 6534.000, + "built_form": 67 + } + }, + { + "pk": 124, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.7026000000, + "net_built_up_area": 2776.950, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750000000, + "building_use_definition": 7, + "gross_built_up_area": 3267.000, + "built_form": 68 + } + }, + { + "pk": 125, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4810400000, + "net_built_up_area": 1110.780, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0300000000, + "building_use_definition": 10, + "gross_built_up_area": 1306.800, + "built_form": 68 + } + }, + { + "pk": 126, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.6267600000, + "net_built_up_area": 7220.070, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.6500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1950000000, + "building_use_definition": 9, + "gross_built_up_area": 8494.200, + "built_form": 68 + } + }, + { + "pk": 127, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.0698800000, + "net_built_up_area": 10552.410, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2850000000, + "building_use_definition": 9, + "gross_built_up_area": 12414.600, + "built_form": 69 + } + }, + { + "pk": 128, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1107800000, + "net_built_up_area": 555.390, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0150000000, + "building_use_definition": 15, + "gross_built_up_area": 653.400, + "built_form": 69 + } + }, + { + "pk": 129, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.7249760000, + "net_built_up_area": 1293.732, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1500000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0300000000, + "building_use_definition": 7, + "gross_built_up_area": 1306.800, + "built_form": 70 + } + }, + { + "pk": 130, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8624880000, + "net_built_up_area": 431.244, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0100000000, + "building_use_definition": 8, + "gross_built_up_area": 435.600, + "built_form": 70 + } + }, + { + "pk": 131, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.1998720000, + "net_built_up_area": 6899.904, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.8000000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.1600000000, + "building_use_definition": 9, + "gross_built_up_area": 6969.600, + "built_form": 70 + } + }, + { + "pk": 132, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.3232000000, + "net_built_up_area": 1742.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0400000000, + "building_use_definition": 7, + "gross_built_up_area": 1742.400, + "built_form": 71 + } + }, + { + "pk": 133, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.8080000000, + "net_built_up_area": 4356.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.5000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 10, + "gross_built_up_area": 4356.000, + "built_form": 71 + } + }, + { + "pk": 134, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.4848000000, + "net_built_up_area": 2613.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.3000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0600000000, + "building_use_definition": 9, + "gross_built_up_area": 2613.600, + "built_form": 71 + } + }, + { + "pk": 135, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 162.6258857143, + "net_built_up_area": 125221.932, + "vacancy_rate": 0.000, + "square_feet_per_unit": 770.000, + "percent": 0.8900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.3820000000, + "building_use_definition": 14, + "gross_built_up_area": 147319.920, + "built_form": 72 + } + }, + { + "pk": 136, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 32.5828800000, + "net_built_up_area": 15476.868, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4180000000, + "building_use_definition": 9, + "gross_built_up_area": 18208.080, + "built_form": 72 + } + }, + { + "pk": 137, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 159.6410869565, + "net_built_up_area": 110152.350, + "vacancy_rate": 0.000, + "square_feet_per_unit": 690.000, + "percent": 0.8500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.9750000000, + "building_use_definition": 14, + "gross_built_up_area": 129591.000, + "built_form": 73 + } + }, + { + "pk": 138, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.9234736842, + "net_built_up_area": 19438.650, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5250000000, + "building_use_definition": 9, + "gross_built_up_area": 22869.000, + "built_form": 73 + } + }, + { + "pk": 139, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 197.2251600000, + "net_built_up_area": 118335.096, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.1960000000, + "building_use_definition": 14, + "gross_built_up_area": 139217.760, + "built_form": 74 + } + }, + { + "pk": 140, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.9016926316, + "net_built_up_area": 7553.304, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2040000000, + "building_use_definition": 9, + "gross_built_up_area": 8886.240, + "built_form": 74 + } + }, + { + "pk": 141, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.5200000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 935.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 14, + "gross_built_up_area": 52272.000, + "built_form": 75 + } + }, + { + "pk": 142, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 46.7696842105, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 9, + "gross_built_up_area": 26136.000, + "built_form": 75 + } + }, + { + "pk": 143, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 148.1040000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 15, + "gross_built_up_area": 52272.000, + "built_form": 75 + } + }, + { + "pk": 144, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 96.3568192771, + "net_built_up_area": 79976.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 830.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.1600000000, + "building_use_definition": 14, + "gross_built_up_area": 94089.600, + "built_form": 76 + } + }, + { + "pk": 145, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.7078736842, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 9, + "gross_built_up_area": 10454.400, + "built_form": 76 + } + }, + { + "pk": 146, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 31.0590720000, + "net_built_up_area": 34164.979, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1100.000, + "percent": 0.7600000, + "efficiency": 0.8600, + "household_size": 2.500, + "floor_area_ratio": 0.9120000000, + "building_use_definition": 14, + "gross_built_up_area": 39726.720, + "built_form": 77 + } + }, + { + "pk": 147, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.6966400000, + "net_built_up_area": 10663.488, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.2400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2880000000, + "building_use_definition": 9, + "gross_built_up_area": 12545.280, + "built_form": 77 + } + }, + { + "pk": 148, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.5898947368, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 7, + "gross_built_up_area": 8712.000, + "built_form": 78 + } + }, + { + "pk": 149, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 177.7248000000, + "net_built_up_area": 133293.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.6000000000, + "building_use_definition": 14, + "gross_built_up_area": 156816.000, + "built_form": 78 + } + }, + { + "pk": 150, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.5898947368, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 9, + "gross_built_up_area": 8712.000, + "built_form": 78 + } + }, + { + "pk": 151, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 52.2474129821, + "net_built_up_area": 55539.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1063.000, + "percent": 0.7500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 14, + "gross_built_up_area": 65340.000, + "built_form": 79 + } + }, + { + "pk": 152, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 38.9747368421, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 9, + "gross_built_up_area": 21780.000, + "built_form": 79 + } + }, + { + "pk": 153, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 153.2358778626, + "net_built_up_area": 120443.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 786.000, + "percent": 0.7000000, + "efficiency": 0.7900, + "household_size": 2.500, + "floor_area_ratio": 3.5000000000, + "building_use_definition": 14, + "gross_built_up_area": 152460.000, + "built_form": 80 + } + }, + { + "pk": 154, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 98.6040000000, + "net_built_up_area": 54232.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.3000000, + "efficiency": 0.8300, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 9, + "gross_built_up_area": 65340.000, + "built_form": 80 + } + }, + { + "pk": 155, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 279.3780000000, + "net_built_up_area": 175170.006, + "vacancy_rate": 0.000, + "square_feet_per_unit": 627.000, + "percent": 0.8300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.7310000000, + "building_use_definition": 14, + "gross_built_up_area": 206082.360, + "built_form": 81 + } + }, + { + "pk": 156, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 75.5330400000, + "net_built_up_area": 35878.194, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9690000000, + "building_use_definition": 9, + "gross_built_up_area": 42209.640, + "built_form": 81 + } + }, + { + "pk": 157, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 166.6170000000, + "net_built_up_area": 99970.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.7000000000, + "building_use_definition": 14, + "gross_built_up_area": 117612.000, + "built_form": 82 + } + }, + { + "pk": 158, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.3848421053, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 9, + "gross_built_up_area": 13068.000, + "built_form": 82 + } + }, + { + "pk": 159, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.7816042105, + "net_built_up_area": 3221.262, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0870000000, + "building_use_definition": 7, + "gross_built_up_area": 3789.720, + "built_form": 83 + } + }, + { + "pk": 160, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 139.8587142857, + "net_built_up_area": 99859.122, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.000, + "percent": 0.9300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.6970000000, + "building_use_definition": 14, + "gross_built_up_area": 117481.320, + "built_form": 83 + } + }, + { + "pk": 161, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.0421389474, + "net_built_up_area": 4295.016, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1160000000, + "building_use_definition": 9, + "gross_built_up_area": 5052.960, + "built_form": 83 + } + }, + { + "pk": 162, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 194.0851255814, + "net_built_up_area": 166913.208, + "vacancy_rate": 0.000, + "square_feet_per_unit": 860.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5080000000, + "building_use_definition": 14, + "gross_built_up_area": 196368.480, + "built_form": 84 + } + }, + { + "pk": 163, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.1713515789, + "net_built_up_area": 3406.392, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0920000000, + "building_use_definition": 9, + "gross_built_up_area": 4007.520, + "built_form": 84 + } + }, + { + "pk": 164, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 191.6945586735, + "net_built_up_area": 150288.534, + "vacancy_rate": 0.000, + "square_feet_per_unit": 784.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.0590000000, + "building_use_definition": 14, + "gross_built_up_area": 176810.040, + "built_form": 85 + } + }, + { + "pk": 165, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.1959284211, + "net_built_up_area": 1518.066, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0410000000, + "building_use_definition": 9, + "gross_built_up_area": 1785.960, + "built_form": 85 + } + }, + { + "pk": 166, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.9254400000, + "net_built_up_area": 7108.992, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1920000000, + "building_use_definition": 7, + "gross_built_up_area": 8363.520, + "built_form": 86 + } + }, + { + "pk": 167, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 149.7680898876, + "net_built_up_area": 106634.880, + "vacancy_rate": 0.000, + "square_feet_per_unit": 712.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.8800000000, + "building_use_definition": 14, + "gross_built_up_area": 125452.800, + "built_form": 86 + } + }, + { + "pk": 168, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.6169600000, + "net_built_up_area": 4739.328, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1280000000, + "building_use_definition": 9, + "gross_built_up_area": 5575.680, + "built_form": 86 + } + }, + { + "pk": 169, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 275.2862453532, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 269.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 15, + "gross_built_up_area": 87120.000, + "built_form": 87 + } + }, + { + "pk": 170, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 476.0485714286, + "net_built_up_area": 166617.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 15, + "gross_built_up_area": 196020.000, + "built_form": 88 + } + }, + { + "pk": 171, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 488.4280851064, + "net_built_up_area": 114780.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 235.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.1000000000, + "building_use_definition": 15, + "gross_built_up_area": 135036.000, + "built_form": 89 + } + }, + { + "pk": 172, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 35.5449600000, + "net_built_up_area": 17772.480, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4800000000, + "building_use_definition": 7, + "gross_built_up_area": 20908.800, + "built_form": 90 + } + }, + { + "pk": 173, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 71.0899200000, + "net_built_up_area": 71089.920, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.9200000000, + "building_use_definition": 14, + "gross_built_up_area": 83635.200, + "built_form": 90 + } + }, + { + "pk": 174, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 35.5449600000, + "net_built_up_area": 17772.480, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4800000000, + "building_use_definition": 9, + "gross_built_up_area": 20908.800, + "built_form": 90 + } + }, + { + "pk": 175, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 341.7784615385, + "net_built_up_area": 71089.920, + "vacancy_rate": 0.000, + "square_feet_per_unit": 208.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.9200000000, + "building_use_definition": 15, + "gross_built_up_area": 83635.200, + "built_form": 90 + } + }, + { + "pk": 176, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 128.3568000000, + "net_built_up_area": 48133.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.3000000000, + "building_use_definition": 15, + "gross_built_up_area": 56628.000, + "built_form": 91 + } + }, + { + "pk": 177, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.7345600000, + "net_built_up_area": 10367.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2800000000, + "building_use_definition": 13, + "gross_built_up_area": 12196.800, + "built_form": 92 + } + }, + { + "pk": 178, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 266.5872000000, + "net_built_up_area": 93305.520, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.5200000000, + "building_use_definition": 15, + "gross_built_up_area": 109771.200, + "built_form": 92 + } + }, + { + "pk": 179, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 105.7885714286, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 15, + "gross_built_up_area": 43560.000, + "built_form": 93 + } + }, + { + "pk": 180, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 123.4200000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 15, + "gross_built_up_area": 43560.000, + "built_form": 94 + } + }, + { + "pk": 181, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 133.9988571429, + "net_built_up_area": 70349.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.9000000000, + "building_use_definition": 16, + "gross_built_up_area": 82764.000, + "built_form": 95 + } + }, + { + "pk": 182, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 56.4205714286, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 7, + "gross_built_up_area": 34848.000, + "built_form": 96 + } + }, + { + "pk": 183, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 56.4205714286, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 7, + "gross_built_up_area": 34848.000, + "built_form": 97 + } + }, + { + "pk": 184, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 91.6834285714, + "net_built_up_area": 48133.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.3000000000, + "building_use_definition": 7, + "gross_built_up_area": 56628.000, + "built_form": 98 + } + }, + { + "pk": 185, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 63.4731428571, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 9, + "gross_built_up_area": 39204.000, + "built_form": 99 + } + }, + { + "pk": 186, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 77.5782857143, + "net_built_up_area": 40728.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.1000000000, + "building_use_definition": 9, + "gross_built_up_area": 47916.000, + "built_form": 100 + } + }, + { + "pk": 187, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 28.2102857143, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 9, + "gross_built_up_area": 17424.000, + "built_form": 101 + } + }, + { + "pk": 188, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 63.4731428571, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 15, + "gross_built_up_area": 26136.000, + "built_form": 101 + } + }, + { + "pk": 189, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 82.1185757746, + "net_built_up_area": 58304.189, + "vacancy_rate": 0.000, + "square_feet_per_unit": 710.000, + "percent": 0.7800000, + "efficiency": 0.7800, + "household_size": 2.500, + "floor_area_ratio": 1.7160000000, + "building_use_definition": 14, + "gross_built_up_area": 74748.960, + "built_form": 102 + } + }, + { + "pk": 190, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 43.5716160000, + "net_built_up_area": 19607.227, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.2200000, + "efficiency": 0.9300, + "household_size": 2.500, + "floor_area_ratio": 0.4840000000, + "building_use_definition": 9, + "gross_built_up_area": 21083.040, + "built_form": 102 + } + }, + { + "pk": 191, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 87.0232000000, + "net_built_up_area": 39160.440, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.3100000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8990000000, + "building_use_definition": 7, + "gross_built_up_area": 39160.440, + "built_form": 103 + } + }, + { + "pk": 192, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 135.5358305630, + "net_built_up_area": 50554.865, + "vacancy_rate": 0.000, + "square_feet_per_unit": 373.000, + "percent": 0.6900000, + "efficiency": 0.5800, + "household_size": 2.500, + "floor_area_ratio": 2.0010000000, + "building_use_definition": 14, + "gross_built_up_area": 87163.560, + "built_form": 103 + } + }, + { + "pk": 193, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 107.1282337079, + "net_built_up_area": 47672.064, + "vacancy_rate": 0.000, + "square_feet_per_unit": 445.000, + "percent": 0.4800000, + "efficiency": 0.9500, + "household_size": 2.500, + "floor_area_ratio": 1.1520000000, + "building_use_definition": 7, + "gross_built_up_area": 50181.120, + "built_form": 104 + } + }, + { + "pk": 194, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 172.1491200000, + "net_built_up_area": 51644.736, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.5200000, + "efficiency": 0.9500, + "household_size": 2.500, + "floor_area_ratio": 1.2480000000, + "building_use_definition": 15, + "gross_built_up_area": 54362.880, + "built_form": 104 + } + }, + { + "pk": 195, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.4320000000, + "net_built_up_area": 36285.480, + "vacancy_rate": 0.000, + "square_feet_per_unit": 765.000, + "percent": 0.7000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9800000000, + "building_use_definition": 14, + "gross_built_up_area": 42688.800, + "built_form": 105 + } + }, + { + "pk": 196, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.9182000000, + "net_built_up_area": 12959.100, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3500000000, + "building_use_definition": 9, + "gross_built_up_area": 15246.000, + "built_form": 105 + } + }, + { + "pk": 197, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.4052000000, + "net_built_up_area": 2591.820, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0700000000, + "building_use_definition": 15, + "gross_built_up_area": 3049.200, + "built_form": 105 + } + }, + { + "pk": 198, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.5744421053, + "net_built_up_area": 4072.860, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1100000000, + "building_use_definition": 7, + "gross_built_up_area": 4791.600, + "built_form": 106 + } + }, + { + "pk": 199, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.9580000000, + "net_built_up_area": 18327.870, + "vacancy_rate": 0.000, + "square_feet_per_unit": 765.000, + "percent": 0.4500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4950000000, + "building_use_definition": 14, + "gross_built_up_area": 21562.200, + "built_form": 106 + } + }, + { + "pk": 200, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.4361052632, + "net_built_up_area": 10182.150, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2750000000, + "building_use_definition": 9, + "gross_built_up_area": 11979.000, + "built_form": 106 + } + }, + { + "pk": 201, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.2734857143, + "net_built_up_area": 8145.720, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2200000000, + "building_use_definition": 15, + "gross_built_up_area": 9583.200, + "built_form": 106 + } + }, + { + "pk": 202, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 12959.100, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1190.000, + "percent": 0.7000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3500000000, + "building_use_definition": 14, + "gross_built_up_area": 15246.000, + "built_form": 107 + } + }, + { + "pk": 203, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.7436842105, + "net_built_up_area": 4628.250, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1250000000, + "building_use_definition": 9, + "gross_built_up_area": 5445.000, + "built_form": 107 + } + }, + { + "pk": 204, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6447142857, + "net_built_up_area": 925.650, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0250000000, + "building_use_definition": 15, + "gross_built_up_area": 1089.000, + "built_form": 107 + } + }, + { + "pk": 205, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 83.9749680000, + "net_built_up_area": 83974.968, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.8100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.2680000000, + "building_use_definition": 14, + "gross_built_up_area": 98794.080, + "built_form": 108 + } + }, + { + "pk": 206, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.9526720000, + "net_built_up_area": 22478.702, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.1900000, + "efficiency": 0.9700, + "household_size": 2.500, + "floor_area_ratio": 0.5320000000, + "building_use_definition": 9, + "gross_built_up_area": 23173.920, + "built_form": 108 + } + }, + { + "pk": 207, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 38.9672385882, + "net_built_up_area": 16561.076, + "vacancy_rate": 0.000, + "square_feet_per_unit": 425.000, + "percent": 0.2300000, + "efficiency": 0.8700, + "household_size": 2.500, + "floor_area_ratio": 0.4370000000, + "building_use_definition": 7, + "gross_built_up_area": 19035.720, + "built_form": 109 + } + }, + { + "pk": 208, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 55.5668926431, + "net_built_up_area": 61179.149, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1101.000, + "percent": 0.7700000, + "efficiency": 0.9600, + "household_size": 2.500, + "floor_area_ratio": 1.4630000000, + "building_use_definition": 14, + "gross_built_up_area": 63728.280, + "built_form": 109 + } + }, + { + "pk": 209, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.2055578947, + "net_built_up_area": 6272.640, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2000000, + "efficiency": 0.9000, + "household_size": 2.500, + "floor_area_ratio": 0.1600000000, + "building_use_definition": 7, + "gross_built_up_area": 6969.600, + "built_form": 110 + } + }, + { + "pk": 210, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.1461147695, + "net_built_up_area": 11848.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1063.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3200000000, + "building_use_definition": 14, + "gross_built_up_area": 13939.200, + "built_form": 110 + } + }, + { + "pk": 211, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.4111157895, + "net_built_up_area": 12545.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.4000000, + "efficiency": 0.9000, + "household_size": 2.500, + "floor_area_ratio": 0.3200000000, + "building_use_definition": 9, + "gross_built_up_area": 13939.200, + "built_form": 110 + } + }, + { + "pk": 212, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 74.0520000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 21, + "gross_built_up_area": 52272.000, + "built_form": 111 + } + }, + { + "pk": 213, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 59.2416000000, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 9, + "gross_built_up_area": 34848.000, + "built_form": 111 + } + }, + { + "pk": 214, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.8059717084, + "net_built_up_area": 36581.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 919.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9880000000, + "building_use_definition": 21, + "gross_built_up_area": 43037.280, + "built_form": 112 + } + }, + { + "pk": 215, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 67.5354240000, + "net_built_up_area": 33767.712, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9120000000, + "building_use_definition": 9, + "gross_built_up_area": 39726.720, + "built_form": 112 + } + }, + { + "pk": 216, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.8059717084, + "net_built_up_area": 36581.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 919.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9880000000, + "building_use_definition": 21, + "gross_built_up_area": 43037.280, + "built_form": 113 + } + }, + { + "pk": 217, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 67.5354240000, + "net_built_up_area": 33767.712, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9120000000, + "building_use_definition": 9, + "gross_built_up_area": 39726.720, + "built_form": 113 + } + }, + { + "pk": 218, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.8059717084, + "net_built_up_area": 36581.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 919.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9880000000, + "building_use_definition": 21, + "gross_built_up_area": 43037.280, + "built_form": 114 + } + }, + { + "pk": 219, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 64.3194514286, + "net_built_up_area": 33767.712, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9120000000, + "building_use_definition": 9, + "gross_built_up_area": 39726.720, + "built_form": 114 + } + }, + { + "pk": 220, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 70.7850000000, + "net_built_up_area": 38507.040, + "vacancy_rate": 0.000, + "square_feet_per_unit": 544.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0400000000, + "building_use_definition": 21, + "gross_built_up_area": 45302.400, + "built_form": 115 + } + }, + { + "pk": 221, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 67.7046857143, + "net_built_up_area": 35544.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9600000000, + "building_use_definition": 9, + "gross_built_up_area": 41817.600, + "built_form": 115 + } + }, + { + "pk": 222, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 43.2056549790, + "net_built_up_area": 30805.632, + "vacancy_rate": 0.000, + "square_feet_per_unit": 713.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8320000000, + "building_use_definition": 21, + "gross_built_up_area": 36241.920, + "built_form": 116 + } + }, + { + "pk": 223, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 53.1513420561, + "net_built_up_area": 28435.968, + "vacancy_rate": 0.000, + "square_feet_per_unit": 535.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7680000000, + "building_use_definition": 9, + "gross_built_up_area": 33454.080, + "built_form": 116 + } + }, + { + "pk": 224, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 46.1823120000, + "net_built_up_area": 34636.734, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9500000, + "efficiency": 0.9300, + "household_size": 2.500, + "floor_area_ratio": 0.8550000000, + "building_use_definition": 9, + "gross_built_up_area": 37243.800, + "built_form": 117 + } + }, + { + "pk": 225, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.3323400000, + "net_built_up_area": 1666.170, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0450000000, + "building_use_definition": 15, + "gross_built_up_area": 1960.200, + "built_form": 117 + } + }, + { + "pk": 226, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.7499200000, + "net_built_up_area": 4312.440, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 7, + "gross_built_up_area": 4356.000, + "built_form": 118 + } + }, + { + "pk": 227, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.4052000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 16, + "gross_built_up_area": 4356.000, + "built_form": 118 + } + }, + { + "pk": 228, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.2497600000, + "net_built_up_area": 12937.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.6000000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 9, + "gross_built_up_area": 13068.000, + "built_form": 118 + } + }, + { + "pk": 229, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.3672800000, + "net_built_up_area": 7775.460, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.7000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2100000000, + "building_use_definition": 9, + "gross_built_up_area": 9147.600, + "built_form": 119 + } + }, + { + "pk": 230, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.6646800000, + "net_built_up_area": 3332.340, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.3000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0900000000, + "building_use_definition": 15, + "gross_built_up_area": 3920.400, + "built_form": 119 + } + }, + { + "pk": 231, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.6646800000, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 9, + "gross_built_up_area": 7840.800, + "built_form": 120 + } + }, + { + "pk": 232, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.8862400000, + "net_built_up_area": 4443.120, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1200000000, + "building_use_definition": 15, + "gross_built_up_area": 5227.200, + "built_form": 120 + } + }, + { + "pk": 233, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 3332.340, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.3000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0900000000, + "building_use_definition": 7, + "gross_built_up_area": 3920.400, + "built_form": 121 + } + }, + { + "pk": 234, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.3672800000, + "net_built_up_area": 7775.460, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.7000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2100000000, + "building_use_definition": 9, + "gross_built_up_area": 9147.600, + "built_form": 121 + } + }, + { + "pk": 235, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.0888320000, + "net_built_up_area": 3066.624, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8800, + "household_size": 2.500, + "floor_area_ratio": 0.0800000000, + "building_use_definition": 7, + "gross_built_up_area": 3484.800, + "built_form": 122 + } + }, + { + "pk": 236, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.0888320000, + "net_built_up_area": 3066.624, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8800, + "household_size": 2.500, + "floor_area_ratio": 0.0800000000, + "building_use_definition": 10, + "gross_built_up_area": 3484.800, + "built_form": 122 + } + }, + { + "pk": 237, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.2664960000, + "net_built_up_area": 9199.872, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.6000000, + "efficiency": 0.8800, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 9, + "gross_built_up_area": 10454.400, + "built_form": 122 + } + }, + { + "pk": 238, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9200000000, + "net_built_up_area": 19800.002, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4545455000, + "building_use_definition": 11, + "gross_built_up_area": 19800.002, + "built_form": 123 + } + }, + { + "pk": 239, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.6800000000, + "net_built_up_area": 17569.199, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1815.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4033333000, + "building_use_definition": 11, + "gross_built_up_area": 17569.199, + "built_form": 124 + } + }, + { + "pk": 240, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9200000000, + "net_built_up_area": 17424.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2200.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 11, + "gross_built_up_area": 17424.000, + "built_form": 125 + } + }, + { + "pk": 241, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.7120000000, + "net_built_up_area": 21780.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 11, + "gross_built_up_area": 21780.000, + "built_form": 126 + } + }, + { + "pk": 242, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.8000000000, + "net_built_up_area": 11739.198, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1334.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2694949000, + "building_use_definition": 11, + "gross_built_up_area": 11739.198, + "built_form": 127 + } + }, + { + "pk": 243, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9200000000, + "net_built_up_area": 19007.998, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2400.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4363636000, + "building_use_definition": 11, + "gross_built_up_area": 19007.998, + "built_form": 128 + } + }, + { + "pk": 244, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9200000000, + "net_built_up_area": 15127.199, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1910.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3472727000, + "building_use_definition": 11, + "gross_built_up_area": 15127.199, + "built_form": 129 + } + }, + { + "pk": 245, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 83.5958607714, + "net_built_up_area": 88862.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1063.000, + "percent": 0.8000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.4000000000, + "building_use_definition": 14, + "gross_built_up_area": 104544.000, + "built_form": 130 + } + }, + { + "pk": 246, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 46.7696842105, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 9, + "gross_built_up_area": 26136.000, + "built_form": 130 + } + }, + { + "pk": 247, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 42.5604126316, + "net_built_up_area": 20216.196, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5460000000, + "building_use_definition": 7, + "gross_built_up_area": 23783.760, + "built_form": 131 + } + }, + { + "pk": 248, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 249.7794984227, + "net_built_up_area": 316720.404, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1268.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.5540000000, + "building_use_definition": 14, + "gross_built_up_area": 372612.240, + "built_form": 131 + } + }, + { + "pk": 249, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 34.6095663158, + "net_built_up_area": 16439.544, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4440000000, + "building_use_definition": 7, + "gross_built_up_area": 19340.640, + "built_form": 132 + } + }, + { + "pk": 250, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 158.7266317241, + "net_built_up_area": 230153.616, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1450.000, + "percent": 0.8400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 6.2160000000, + "building_use_definition": 14, + "gross_built_up_area": 270768.960, + "built_form": 132 + } + }, + { + "pk": 251, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 91.3308000000, + "net_built_up_area": 27399.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7400000000, + "building_use_definition": 15, + "gross_built_up_area": 32234.400, + "built_form": 132 + } + }, + { + "pk": 252, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 28.6783200000, + "net_built_up_area": 15773.076, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4260000000, + "building_use_definition": 7, + "gross_built_up_area": 18556.560, + "built_form": 133 + } + }, + { + "pk": 253, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 136.2220200000, + "net_built_up_area": 149844.222, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1100.000, + "percent": 0.5700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.0470000000, + "building_use_definition": 14, + "gross_built_up_area": 176287.320, + "built_form": 133 + } + }, + { + "pk": 254, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 28.6783200000, + "net_built_up_area": 15773.076, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4260000000, + "building_use_definition": 9, + "gross_built_up_area": 18556.560, + "built_form": 133 + } + }, + { + "pk": 255, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 271.6474200000, + "net_built_up_area": 81494.226, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.3100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.2010000000, + "building_use_definition": 15, + "gross_built_up_area": 95875.560, + "built_form": 133 + } + }, + { + "pk": 256, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 34.4107806691, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 7, + "gross_built_up_area": 21780.000, + "built_form": 134 + } + }, + { + "pk": 257, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 619.3940520446, + "net_built_up_area": 166617.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 269.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 15, + "gross_built_up_area": 196020.000, + "built_form": 134 + } + }, + { + "pk": 258, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1135.4640000000, + "net_built_up_area": 170319.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 150.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.6000000000, + "building_use_definition": 15, + "gross_built_up_area": 200376.000, + "built_form": 135 + } + }, + { + "pk": 259, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.6255680000, + "net_built_up_area": 6812.784, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1840000000, + "building_use_definition": 7, + "gross_built_up_area": 8015.040, + "built_form": 136 + } + }, + { + "pk": 260, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.4383520000, + "net_built_up_area": 10219.176, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2760000000, + "building_use_definition": 9, + "gross_built_up_area": 12022.560, + "built_form": 136 + } + }, + { + "pk": 261, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1021.9176000000, + "net_built_up_area": 153287.640, + "vacancy_rate": 0.000, + "square_feet_per_unit": 150.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.1400000000, + "building_use_definition": 15, + "gross_built_up_area": 180338.400, + "built_form": 136 + } + }, + { + "pk": 262, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.7643122677, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 7, + "gross_built_up_area": 8712.000, + "built_form": 137 + } + }, + { + "pk": 263, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.6464684015, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 9, + "gross_built_up_area": 13068.000, + "built_form": 137 + } + }, + { + "pk": 264, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 619.3940520446, + "net_built_up_area": 166617.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 269.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 15, + "gross_built_up_area": 196020.000, + "built_form": 137 + } + }, + { + "pk": 265, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.1078000000, + "net_built_up_area": 5553.900, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1500000000, + "building_use_definition": 9, + "gross_built_up_area": 6534.000, + "built_form": 138 + } + }, + { + "pk": 266, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 301.4974285714, + "net_built_up_area": 105524.100, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.8500000000, + "building_use_definition": 15, + "gross_built_up_area": 124146.000, + "built_form": 138 + } + }, + { + "pk": 267, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 592.4160000000, + "net_built_up_area": 207345.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.6000000000, + "building_use_definition": 15, + "gross_built_up_area": 243936.000, + "built_form": 139 + } + }, + { + "pk": 268, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.2944200000, + "net_built_up_area": 3147.210, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0850000000, + "building_use_definition": 7, + "gross_built_up_area": 3702.600, + "built_form": 140 + } + }, + { + "pk": 269, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 199.3233000000, + "net_built_up_area": 59796.990, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.6150000000, + "building_use_definition": 15, + "gross_built_up_area": 70349.400, + "built_form": 140 + } + }, + { + "pk": 270, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6928000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 8, + "gross_built_up_area": 8712.000, + "built_form": 141 + } + }, + { + "pk": 271, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8513000000, + "net_built_up_area": 9256.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2500000000, + "building_use_definition": 8, + "gross_built_up_area": 10890.000, + "built_form": 142 + } + }, + { + "pk": 272, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.9747200000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 8, + "gross_built_up_area": 8712.000, + "built_form": 143 + } + }, + { + "pk": 273, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9670714286, + "net_built_up_area": 2776.950, + "vacancy_rate": 0.000, + "square_feet_per_unit": 700.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750000000, + "building_use_definition": 18, + "gross_built_up_area": 3267.000, + "built_form": 144 + } + }, + { + "pk": 274, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 27.7695000000, + "net_built_up_area": 8330.850, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.7500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2250000000, + "building_use_definition": 15, + "gross_built_up_area": 9801.000, + "built_form": 144 + } + }, + { + "pk": 275, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 86.3940000000, + "net_built_up_area": 25918.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 15, + "gross_built_up_area": 30492.000, + "built_form": 145 + } + }, + { + "pk": 276, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 42.3154285714, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 15, + "gross_built_up_area": 17424.000, + "built_form": 146 + } + }, + { + "pk": 277, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 275.2862453532, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 269.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 15, + "gross_built_up_area": 87120.000, + "built_form": 147 + } + }, + { + "pk": 278, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.9182000000, + "net_built_up_area": 12959.100, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3500000000, + "building_use_definition": 15, + "gross_built_up_area": 15246.000, + "built_form": 148 + } + }, + { + "pk": 279, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.7026000000, + "net_built_up_area": 1851.300, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0500000000, + "building_use_definition": 9, + "gross_built_up_area": 2178.000, + "built_form": 149 + } + }, + { + "pk": 280, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 117.2490000000, + "net_built_up_area": 35174.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9500000000, + "building_use_definition": 15, + "gross_built_up_area": 41382.000, + "built_form": 149 + } + }, + { + "pk": 281, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 123.4200000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 15, + "gross_built_up_area": 43560.000, + "built_form": 150 + } + }, + { + "pk": 282, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.3680000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 15, + "gross_built_up_area": 17424.000, + "built_form": 151 + } + }, + { + "pk": 283, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.8862400000, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 18, + "gross_built_up_area": 10454.400, + "built_form": 152 + } + }, + { + "pk": 284, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.7977600000, + "net_built_up_area": 5924.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1600000000, + "building_use_definition": 15, + "gross_built_up_area": 6969.600, + "built_form": 152 + } + }, + { + "pk": 285, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 4443.120, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1200000000, + "building_use_definition": 18, + "gross_built_up_area": 5227.200, + "built_form": 153 + } + }, + { + "pk": 286, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.7724800000, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 15, + "gross_built_up_area": 7840.800, + "built_form": 153 + } + }, + { + "pk": 287, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.2215600000, + "net_built_up_area": 2221.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0600000000, + "building_use_definition": 18, + "gross_built_up_area": 2613.600, + "built_form": 154 + } + }, + { + "pk": 288, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.8000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 15, + "gross_built_up_area": 10454.400, + "built_form": 154 + } + }, + { + "pk": 289, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 16.4560000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 15, + "gross_built_up_area": 8712.000, + "built_form": 155 + } + }, + { + "pk": 290, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.0492000000, + "net_built_up_area": 1524.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 3.5000000000, + "building_use_definition": 10, + "gross_built_up_area": 152460.000, + "built_form": 156 + } + }, + { + "pk": 291, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9204000000, + "net_built_up_area": 1960.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 10, + "gross_built_up_area": 196020.000, + "built_form": 157 + } + }, + { + "pk": 292, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1780000000, + "net_built_up_area": 1089.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 2.5000000000, + "building_use_definition": 10, + "gross_built_up_area": 108900.000, + "built_form": 158 + } + }, + { + "pk": 293, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9204000000, + "net_built_up_area": 1960.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 10, + "gross_built_up_area": 196020.000, + "built_form": 159 + } + }, + { + "pk": 294, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.7916000000, + "net_built_up_area": 2395.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 5.5000000000, + "building_use_definition": 10, + "gross_built_up_area": 239580.000, + "built_form": 160 + } + }, + { + "pk": 295, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.8483200000, + "net_built_up_area": 11848.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3200000000, + "building_use_definition": 10, + "gross_built_up_area": 13939.200, + "built_form": 161 + } + }, + { + "pk": 296, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.3420000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 14, + "gross_built_up_area": 17424.000, + "built_form": 161 + } + }, + { + "pk": 297, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9620800000, + "net_built_up_area": 2962.080, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0800000000, + "building_use_definition": 9, + "gross_built_up_area": 3484.800, + "built_form": 161 + } + }, + { + "pk": 298, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.8483200000, + "net_built_up_area": 11848.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3200000000, + "building_use_definition": 10, + "gross_built_up_area": 13939.200, + "built_form": 162 + } + }, + { + "pk": 299, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1500.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 14, + "gross_built_up_area": 34848.000, + "built_form": 162 + } + }, + { + "pk": 300, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.7724800000, + "net_built_up_area": 17772.480, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.3000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4800000000, + "building_use_definition": 9, + "gross_built_up_area": 20908.800, + "built_form": 162 + } + }, + { + "pk": 301, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 31.9904640000, + "net_built_up_area": 31990.464, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.3600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8640000000, + "building_use_definition": 10, + "gross_built_up_area": 37635.840, + "built_form": 163 + } + }, + { + "pk": 302, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1800.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 14, + "gross_built_up_area": 52272.000, + "built_form": 163 + } + }, + { + "pk": 303, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.4407360000, + "net_built_up_area": 12440.736, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.1400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3360000000, + "building_use_definition": 9, + "gross_built_up_area": 14636.160, + "built_form": 163 + } + }, + { + "pk": 304, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.1664000000, + "net_built_up_area": 9583.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.4000000, + "efficiency": 0.1000, + "household_size": 2.500, + "floor_area_ratio": 2.2000000000, + "building_use_definition": 10, + "gross_built_up_area": 95832.000, + "built_form": 164 + } + }, + { + "pk": 305, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.7916000000, + "net_built_up_area": 2395.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.1000, + "household_size": 2.500, + "floor_area_ratio": 0.5500000000, + "building_use_definition": 9, + "gross_built_up_area": 23958.000, + "built_form": 164 + } + }, + { + "pk": 306, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 45.6342857143, + "net_built_up_area": 23958.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.5000000, + "efficiency": 0.2000, + "household_size": 2.500, + "floor_area_ratio": 2.7500000000, + "building_use_definition": 15, + "gross_built_up_area": 119790.000, + "built_form": 164 + } + }, + { + "pk": 307, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 27.7477200000, + "net_built_up_area": 13873.860, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.7000000, + "efficiency": 0.0700, + "household_size": 2.500, + "floor_area_ratio": 4.5500000000, + "building_use_definition": 10, + "gross_built_up_area": 198198.000, + "built_form": 165 + } + }, + { + "pk": 308, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.8918800000, + "net_built_up_area": 5945.940, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.3000000, + "efficiency": 0.0700, + "household_size": 2.500, + "floor_area_ratio": 1.9500000000, + "building_use_definition": 9, + "gross_built_up_area": 84942.000, + "built_form": 165 + } + }, + { + "pk": 309, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.1570000000, + "net_built_up_area": 7078.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.6500000, + "efficiency": 0.1000, + "household_size": 2.500, + "floor_area_ratio": 1.6250000000, + "building_use_definition": 10, + "gross_built_up_area": 70785.000, + "built_form": 166 + } + }, + { + "pk": 310, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.6230000000, + "net_built_up_area": 3811.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.3500000, + "efficiency": 0.1000, + "household_size": 2.500, + "floor_area_ratio": 0.8750000000, + "building_use_definition": 9, + "gross_built_up_area": 38115.000, + "built_form": 166 + } + }, + { + "pk": 311, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.9747200000, + "net_built_up_area": 1481.040, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0400000000, + "building_use_definition": 7, + "gross_built_up_area": 1742.400, + "built_form": 167 + } + }, + { + "pk": 312, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9620800000, + "net_built_up_area": 2221.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0600000000, + "building_use_definition": 10, + "gross_built_up_area": 2613.600, + "built_form": 167 + } + }, + { + "pk": 313, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.8104000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.7500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 9, + "gross_built_up_area": 13068.000, + "built_form": 167 + } + }, + { + "pk": 314, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9620800000, + "net_built_up_area": 2221.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0600000000, + "building_use_definition": 7, + "gross_built_up_area": 2613.600, + "built_form": 168 + } + }, + { + "pk": 315, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.7266880000, + "net_built_up_area": 4295.016, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1160000000, + "building_use_definition": 10, + "gross_built_up_area": 5052.960, + "built_form": 168 + } + }, + { + "pk": 316, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.0584320000, + "net_built_up_area": 8293.824, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.5600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2240000000, + "building_use_definition": 9, + "gross_built_up_area": 9757.440, + "built_form": 168 + } + }, + { + "pk": 317, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 7, + "gross_built_up_area": 4356.000, + "built_form": 169 + } + }, + { + "pk": 318, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.1457200000, + "net_built_up_area": 6109.290, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.3300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1650000000, + "building_use_definition": 10, + "gross_built_up_area": 7187.400, + "built_form": 169 + } + }, + { + "pk": 319, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.6014800000, + "net_built_up_area": 8701.110, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.4700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2350000000, + "building_use_definition": 9, + "gross_built_up_area": 10236.600, + "built_form": 169 + } + }, + { + "pk": 320, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 7, + "gross_built_up_area": 4356.000, + "built_form": 170 + } + }, + { + "pk": 321, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.1710000000, + "net_built_up_area": 4628.250, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1250000000, + "building_use_definition": 10, + "gross_built_up_area": 5445.000, + "built_form": 170 + } + }, + { + "pk": 322, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.5762000000, + "net_built_up_area": 10182.150, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.5500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2750000000, + "building_use_definition": 9, + "gross_built_up_area": 11979.000, + "built_form": 170 + } + }, + { + "pk": 323, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.4178400000, + "net_built_up_area": 4813.380, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1300000000, + "building_use_definition": 7, + "gross_built_up_area": 5662.800, + "built_form": 171 + } + }, + { + "pk": 324, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.4684000000, + "net_built_up_area": 1851.300, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0500000000, + "building_use_definition": 10, + "gross_built_up_area": 2178.000, + "built_form": 171 + } + }, + { + "pk": 325, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.7977600000, + "net_built_up_area": 11848.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.6400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3200000000, + "building_use_definition": 9, + "gross_built_up_area": 13939.200, + "built_form": 171 + } + }, + { + "pk": 326, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9620800000, + "net_built_up_area": 2221.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0600000000, + "building_use_definition": 7, + "gross_built_up_area": 2613.600, + "built_form": 172 + } + }, + { + "pk": 327, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4810400000, + "net_built_up_area": 1110.780, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0300000000, + "building_use_definition": 10, + "gross_built_up_area": 1306.800, + "built_form": 172 + } + }, + { + "pk": 328, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.6267600000, + "net_built_up_area": 7220.070, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.6500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1950000000, + "building_use_definition": 9, + "gross_built_up_area": 8494.200, + "built_form": 172 + } + }, + { + "pk": 329, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8513000000, + "net_built_up_area": 555.390, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0150000000, + "building_use_definition": 15, + "gross_built_up_area": 653.400, + "built_form": 172 + } + }, + { + "pk": 330, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 22, + "gross_built_up_area": 43.560, + "built_form": 173 + } + }, + { + "pk": 331, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.741, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000200000, + "building_use_definition": 11, + "gross_built_up_area": 0.871, + "built_form": 174 + } + }, + { + "pk": 332, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0086248800, + "net_built_up_area": 86.249, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0019800000, + "building_use_definition": 22, + "gross_built_up_area": 86.249, + "built_form": 174 + } + }, + { + "pk": 333, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 22, + "gross_built_up_area": 43.560, + "built_form": 175 + } + }, + { + "pk": 334, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 11, + "gross_built_up_area": 0.436, + "built_form": 176 + } + }, + { + "pk": 335, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 22, + "gross_built_up_area": 43.124, + "built_form": 176 + } + }, + { + "pk": 336, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 11, + "gross_built_up_area": 0.436, + "built_form": 177 + } + }, + { + "pk": 337, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 22, + "gross_built_up_area": 43.124, + "built_form": 177 + } + }, + { + "pk": 338, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.741, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000200000, + "building_use_definition": 11, + "gross_built_up_area": 0.871, + "built_form": 178 + } + }, + { + "pk": 339, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042688800, + "net_built_up_area": 42.689, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9800000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009800000, + "building_use_definition": 22, + "gross_built_up_area": 42.689, + "built_form": 178 + } + }, + { + "pk": 340, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 1.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000300000, + "building_use_definition": 11, + "gross_built_up_area": 1.307, + "built_form": 179 + } + }, + { + "pk": 341, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042253200, + "net_built_up_area": 42.253, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9700000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009700000, + "building_use_definition": 22, + "gross_built_up_area": 42.253, + "built_form": 179 + } + }, + { + "pk": 342, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 22, + "gross_built_up_area": 43.560, + "built_form": 180 + } + }, + { + "pk": 343, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 22, + "gross_built_up_area": 43.560, + "built_form": 181 + } + }, + { + "pk": 344, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 1.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000300000, + "building_use_definition": 11, + "gross_built_up_area": 1.307, + "built_form": 182 + } + }, + { + "pk": 345, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0129373200, + "net_built_up_area": 129.373, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0029700000, + "building_use_definition": 22, + "gross_built_up_area": 129.373, + "built_form": 182 + } + }, + { + "pk": 346, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 22, + "gross_built_up_area": 43.560, + "built_form": 183 + } + }, + { + "pk": 347, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 1.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000300000, + "building_use_definition": 11, + "gross_built_up_area": 1.307, + "built_form": 184 + } + }, + { + "pk": 348, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042253200, + "net_built_up_area": 42.253, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9700000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009700000, + "building_use_definition": 22, + "gross_built_up_area": 42.253, + "built_form": 184 + } + }, + { + "pk": 349, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 11, + "gross_built_up_area": 0.436, + "built_form": 185 + } + }, + { + "pk": 350, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 22, + "gross_built_up_area": 43.124, + "built_form": 185 + } + }, + { + "pk": 351, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 1.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000300000, + "building_use_definition": 11, + "gross_built_up_area": 1.307, + "built_form": 186 + } + }, + { + "pk": 352, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042253200, + "net_built_up_area": 42.253, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9700000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009700000, + "building_use_definition": 22, + "gross_built_up_area": 42.253, + "built_form": 186 + } + }, + { + "pk": 353, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.741, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000200000, + "building_use_definition": 11, + "gross_built_up_area": 0.871, + "built_form": 187 + } + }, + { + "pk": 354, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042688800, + "net_built_up_area": 42.689, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9800000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009800000, + "building_use_definition": 22, + "gross_built_up_area": 42.689, + "built_form": 187 + } + }, + { + "pk": 355, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 11, + "gross_built_up_area": 0.436, + "built_form": 188 + } + }, + { + "pk": 356, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 22, + "gross_built_up_area": 43.124, + "built_form": 188 + } + }, + { + "pk": 357, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 22, + "gross_built_up_area": 43.560, + "built_form": 189 + } + }, + { + "pk": 358, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0566280000, + "net_built_up_area": 566.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0130000000, + "building_use_definition": 22, + "gross_built_up_area": 566.280, + "built_form": 190 + } + }, + { + "pk": 359, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0024684000, + "net_built_up_area": 1.851, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000500000, + "building_use_definition": 9, + "gross_built_up_area": 2.178, + "built_form": 191 + } + }, + { + "pk": 360, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0039204000, + "net_built_up_area": 39.204, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009000000, + "building_use_definition": 22, + "gross_built_up_area": 39.204, + "built_form": 191 + } + }, + { + "pk": 361, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0037026000, + "net_built_up_area": 1.851, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000500000, + "building_use_definition": 15, + "gross_built_up_area": 2.178, + "built_form": 191 + } + }, + { + "pk": 362, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 11, + "gross_built_up_area": 0.436, + "built_form": 192 + } + }, + { + "pk": 363, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 22, + "gross_built_up_area": 43.124, + "built_form": 192 + } + }, + { + "pk": 364, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 22, + "gross_built_up_area": 43.560, + "built_form": 193 + } + }, + { + "pk": 365, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 22, + "gross_built_up_area": 43.560, + "built_form": 194 + } + }, + { + "pk": 366, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 22, + "gross_built_up_area": 43.560, + "built_form": 195 + } + }, + { + "pk": 367, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0546242400, + "net_built_up_area": 40.968, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9500000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0009500000, + "building_use_definition": 13, + "gross_built_up_area": 41.382, + "built_form": 196 + } + }, + { + "pk": 368, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0037026000, + "net_built_up_area": 1.851, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000500000, + "building_use_definition": 15, + "gross_built_up_area": 2.178, + "built_form": 196 + } + }, + { + "pk": 369, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0041382000, + "net_built_up_area": 41.382, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9500000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009500000, + "building_use_definition": 23, + "gross_built_up_area": 41.382, + "built_form": 197 + } + }, + { + "pk": 370, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0037026000, + "net_built_up_area": 1.851, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000500000, + "building_use_definition": 15, + "gross_built_up_area": 2.178, + "built_form": 197 + } + }, + { + "pk": 371, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 23, + "gross_built_up_area": 43.560, + "built_form": 198 + } + }, + { + "pk": 372, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0039204000, + "net_built_up_area": 39.204, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009000000, + "building_use_definition": 23, + "gross_built_up_area": 39.204, + "built_form": 199 + } + }, + { + "pk": 373, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0074052000, + "net_built_up_area": 3.703, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0001000000, + "building_use_definition": 15, + "gross_built_up_area": 4.356, + "built_form": 199 + } + }, + { + "pk": 374, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1698840000, + "net_built_up_area": 424.710, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.7500, + "household_size": 2.500, + "floor_area_ratio": 0.0130000000, + "building_use_definition": 24, + "gross_built_up_area": 566.280, + "built_form": 200 + } + }, + { + "pk": 375, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0740520000, + "net_built_up_area": 37.026, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 25, + "gross_built_up_area": 43.560, + "built_form": 201 + } + }, + { + "pk": 376, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5445000000, + "net_built_up_area": 1905.750, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0437500000, + "building_use_definition": 11, + "gross_built_up_area": 1905.750, + "built_form": 202 + } + }, + { + "pk": 377, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2000000000, + "net_built_up_area": 700.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0160698000, + "building_use_definition": 11, + "gross_built_up_area": 700.000, + "built_form": 203 + } + }, + { + "pk": 378, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6666666667, + "net_built_up_area": 2333.335, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0535660000, + "building_use_definition": 11, + "gross_built_up_area": 2333.335, + "built_form": 204 + } + }, + { + "pk": 379, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1000000000, + "net_built_up_area": 350.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0080349000, + "building_use_definition": 11, + "gross_built_up_area": 350.000, + "built_form": 205 + } + }, + { + "pk": 380, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1210000000, + "net_built_up_area": 423.499, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0097222000, + "building_use_definition": 11, + "gross_built_up_area": 423.499, + "built_form": 206 + } + }, + { + "pk": 381, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0500000000, + "net_built_up_area": 174.998, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0040174000, + "building_use_definition": 11, + "gross_built_up_area": 174.998, + "built_form": 207 + } + }, + { + "pk": 382, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0435600000, + "net_built_up_area": 152.460, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0035000000, + "building_use_definition": 11, + "gross_built_up_area": 152.460, + "built_form": 208 + } + }, + { + "pk": 383, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0478681319, + "net_built_up_area": 167.540, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0038462000, + "building_use_definition": 11, + "gross_built_up_area": 167.540, + "built_form": 209 + } + }, + { + "pk": 384, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2074285714, + "net_built_up_area": 726.001, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0166667000, + "building_use_definition": 11, + "gross_built_up_area": 726.001, + "built_form": 210 + } + }, + { + "pk": 385, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4356000000, + "net_built_up_area": 1524.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0350000000, + "building_use_definition": 11, + "gross_built_up_area": 1524.600, + "built_form": 211 + } + }, + { + "pk": 386, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5445000000, + "net_built_up_area": 1361.250, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0312500000, + "building_use_definition": 11, + "gross_built_up_area": 1361.250, + "built_form": 212 + } + }, + { + "pk": 387, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2722500000, + "net_built_up_area": 816.750, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0187500000, + "building_use_definition": 11, + "gross_built_up_area": 816.750, + "built_form": 213 + } + }, + { + "pk": 388, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.5124474845, + "net_built_up_area": 6046.764, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3998.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1388146000, + "building_use_definition": 11, + "gross_built_up_area": 6046.764, + "built_form": 214 + } + }, + { + "pk": 389, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5445000000, + "net_built_up_area": 1633.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0375000000, + "building_use_definition": 11, + "gross_built_up_area": 1633.500, + "built_form": 215 + } + }, + { + "pk": 390, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 108.2505600000, + "net_built_up_area": 59537.808, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.6080000000, + "building_use_definition": 7, + "gross_built_up_area": 70044.480, + "built_form": 216 + } + }, + { + "pk": 391, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 305.3220923077, + "net_built_up_area": 595378.080, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1950.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 16.0800000000, + "building_use_definition": 14, + "gross_built_up_area": 700444.800, + "built_form": 216 + } + }, + { + "pk": 392, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1349.5236480000, + "net_built_up_area": 337380.912, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.3400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 9.1120000000, + "building_use_definition": 15, + "gross_built_up_area": 396918.720, + "built_form": 216 + } + }, + { + "pk": 393, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 71.7135157895, + "net_built_up_area": 34063.920, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9200000000, + "building_use_definition": 7, + "gross_built_up_area": 40075.200, + "built_form": 217 + } + }, + { + "pk": 394, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 112.0933382084, + "net_built_up_area": 306575.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2735.000, + "percent": 0.4500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.2800000000, + "building_use_definition": 14, + "gross_built_up_area": 360676.800, + "built_form": 217 + } + }, + { + "pk": 395, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 973.2548571429, + "net_built_up_area": 340639.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 9.2000000000, + "building_use_definition": 15, + "gross_built_up_area": 400752.000, + "built_form": 217 + } + }, + { + "pk": 396, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 330.5447431579, + "net_built_up_area": 628035.012, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1900.000, + "percent": 0.3300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 16.9620000000, + "building_use_definition": 14, + "gross_built_up_area": 738864.720, + "built_form": 218 + } + }, + { + "pk": 397, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5100.4055520000, + "net_built_up_area": 1275101.388, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.6700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 34.4380000000, + "building_use_definition": 15, + "gross_built_up_area": 1500119.280, + "built_form": 218 + } + }, + { + "pk": 398, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 148.4002080000, + "net_built_up_area": 74200.104, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0040000000, + "building_use_definition": 9, + "gross_built_up_area": 87294.240, + "built_form": 219 + } + }, + { + "pk": 399, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4649.8731840000, + "net_built_up_area": 1162468.296, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 31.3960000000, + "building_use_definition": 15, + "gross_built_up_area": 1367609.760, + "built_form": 219 + } + }, + { + "pk": 400, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.2475600000, + "net_built_up_area": 19623.780, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5300000000, + "building_use_definition": 7, + "gross_built_up_area": 23086.800, + "built_form": 220 + } + }, + { + "pk": 401, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3846.2608800000, + "net_built_up_area": 961565.220, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 25.9700000000, + "building_use_definition": 15, + "gross_built_up_area": 1131253.200, + "built_form": 220 + } + }, + { + "pk": 402, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.3192280000, + "net_built_up_area": 23659.614, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6390000000, + "building_use_definition": 7, + "gross_built_up_area": 27834.840, + "built_form": 221 + } + }, + { + "pk": 403, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3059.9767440000, + "net_built_up_area": 764994.186, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 20.6610000000, + "building_use_definition": 15, + "gross_built_up_area": 899993.160, + "built_form": 221 + } + }, + { + "pk": 404, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 44.4312000000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 7, + "gross_built_up_area": 26136.000, + "built_form": 222 + } + }, + { + "pk": 405, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2873.2176000000, + "net_built_up_area": 718304.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 19.4000000000, + "building_use_definition": 15, + "gross_built_up_area": 845064.000, + "built_form": 222 + } + }, + { + "pk": 406, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.1705280000, + "net_built_up_area": 24585.264, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6640000000, + "building_use_definition": 7, + "gross_built_up_area": 28923.840, + "built_form": 223 + } + }, + { + "pk": 407, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4818.7117440000, + "net_built_up_area": 1204677.936, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 32.5360000000, + "building_use_definition": 15, + "gross_built_up_area": 1417268.160, + "built_form": 223 + } + }, + { + "pk": 408, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.9596960000, + "net_built_up_area": 5479.848, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1480000000, + "building_use_definition": 7, + "gross_built_up_area": 6446.880, + "built_form": 224 + } + }, + { + "pk": 409, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1074.0502080000, + "net_built_up_area": 268512.552, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 7.2520000000, + "building_use_definition": 15, + "gross_built_up_area": 315897.120, + "built_form": 224 + } + }, + { + "pk": 410, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.8068240000, + "net_built_up_area": 13403.412, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3620000000, + "building_use_definition": 7, + "gross_built_up_area": 15768.720, + "built_form": 225 + } + }, + { + "pk": 411, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2627.0687520000, + "net_built_up_area": 656767.188, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 17.7380000000, + "building_use_definition": 15, + "gross_built_up_area": 772667.280, + "built_form": 225 + } + }, + { + "pk": 412, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 166.4863200000, + "net_built_up_area": 83243.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.9800, + "household_size": 2.500, + "floor_area_ratio": 1.9500000000, + "building_use_definition": 7, + "gross_built_up_area": 84942.000, + "built_form": 226 + } + }, + { + "pk": 413, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2599.2252000000, + "net_built_up_area": 649806.300, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 17.5500000000, + "building_use_definition": 15, + "gross_built_up_area": 764478.000, + "built_form": 226 + } + }, + { + "pk": 414, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 37.1636431227, + "net_built_up_area": 19994.040, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5400000000, + "building_use_definition": 7, + "gross_built_up_area": 23522.400, + "built_form": 227 + } + }, + { + "pk": 415, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 264.5607770270, + "net_built_up_area": 313239.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.4600000000, + "building_use_definition": 14, + "gross_built_up_area": 368517.600, + "built_form": 227 + } + }, + { + "pk": 416, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 545.0088785047, + "net_built_up_area": 699791.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1284.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 18.9000000000, + "building_use_definition": 14, + "gross_built_up_area": 823284.000, + "built_form": 228 + } + }, + { + "pk": 417, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.1650400000, + "net_built_up_area": 11774.268, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3180000000, + "building_use_definition": 7, + "gross_built_up_area": 13852.080, + "built_form": 229 + } + }, + { + "pk": 418, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 672.4232307692, + "net_built_up_area": 576939.132, + "vacancy_rate": 0.000, + "square_feet_per_unit": 858.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 15.5820000000, + "building_use_definition": 14, + "gross_built_up_area": 678751.920, + "built_form": 229 + } + }, + { + "pk": 419, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 458.3936603774, + "net_built_up_area": 728845.920, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1590.000, + "percent": 1.0000000, + "efficiency": 0.9400, + "household_size": 2.500, + "floor_area_ratio": 17.8000000000, + "building_use_definition": 14, + "gross_built_up_area": 775368.000, + "built_form": 230 + } + }, + { + "pk": 420, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 395.6855793991, + "net_built_up_area": 921947.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2330.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 24.9000000000, + "building_use_definition": 14, + "gross_built_up_area": 1084644.000, + "built_form": 231 + } + }, + { + "pk": 421, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 21780.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 26, + "gross_built_up_area": 21780.000, + "built_form": 232 + } + }, + { + "pk": 422, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 19602.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1800.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 26, + "gross_built_up_area": 19602.000, + "built_form": 233 + } + }, + { + "pk": 423, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.4160688666, + "net_built_up_area": 29592.050, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2841.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.6793400000, + "building_use_definition": 26, + "gross_built_up_area": 29592.050, + "built_form": 234 + } + }, + { + "pk": 424, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.2978723404, + "net_built_up_area": 26671.487, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2590.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.6122931000, + "building_use_definition": 26, + "gross_built_up_area": 26671.487, + "built_form": 235 + } + }, + { + "pk": 425, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 16335.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3750000000, + "building_use_definition": 26, + "gross_built_up_area": 16335.000, + "built_form": 236 + } + }, + { + "pk": 426, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 35937.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3300.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8250000000, + "building_use_definition": 26, + "gross_built_up_area": 35937.000, + "built_form": 237 + } + }, + { + "pk": 427, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 91.0014020270, + "net_built_up_area": 107745.660, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.9100000000, + "building_use_definition": 21, + "gross_built_up_area": 126759.600, + "built_form": 238 + } + }, + { + "pk": 428, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.1939405204, + "net_built_up_area": 3332.340, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0900000000, + "building_use_definition": 9, + "gross_built_up_area": 3920.400, + "built_form": 238 + } + }, + { + "pk": 429, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 124.1460000000, + "net_built_up_area": 94971.690, + "vacancy_rate": 0.000, + "square_feet_per_unit": 765.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.5650000000, + "building_use_definition": 14, + "gross_built_up_area": 111731.400, + "built_form": 239 + } + }, + { + "pk": 430, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.9940400000, + "net_built_up_area": 4998.510, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1350000000, + "building_use_definition": 9, + "gross_built_up_area": 5880.600, + "built_form": 239 + } + }, + { + "pk": 431, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 59.2416000000, + "net_built_up_area": 68127.840, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1150.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.8400000000, + "building_use_definition": 14, + "gross_built_up_area": 80150.400, + "built_form": 240 + } + }, + { + "pk": 432, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.8483200000, + "net_built_up_area": 5924.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1600000000, + "building_use_definition": 9, + "gross_built_up_area": 6969.600, + "built_form": 240 + } + }, + { + "pk": 433, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 103.5135483871, + "net_built_up_area": 96267.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 930.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.6000000000, + "building_use_definition": 14, + "gross_built_up_area": 113256.000, + "built_form": 241 + } + }, + { + "pk": 434, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 62.3209900990, + "net_built_up_area": 62944.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1010.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.7000000000, + "building_use_definition": 14, + "gross_built_up_area": 74052.000, + "built_form": 242 + } + }, + { + "pk": 435, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 78.7787234043, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 940.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 87120.000, + "built_form": 243 + } + }, + { + "pk": 436, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 111.0780000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 130680.000, + "built_form": 244 + } + }, + { + "pk": 437, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.6048571429, + "net_built_up_area": 66646.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1400.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.8000000000, + "building_use_definition": 14, + "gross_built_up_area": 78408.000, + "built_form": 245 + } + }, + { + "pk": 438, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.8104000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 9, + "gross_built_up_area": 8712.000, + "built_form": 245 + } + }, + { + "pk": 439, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 199.9404000000, + "net_built_up_area": 149955.300, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.0500000000, + "building_use_definition": 14, + "gross_built_up_area": 176418.000, + "built_form": 246 + } + }, + { + "pk": 440, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 33.3234000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 9, + "gross_built_up_area": 19602.000, + "built_form": 246 + } + }, + { + "pk": 441, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.2594458438, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1191.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 12, + "gross_built_up_area": 34848.000, + "built_form": 247 + } + }, + { + "pk": 442, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.2320000000, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 12, + "gross_built_up_area": 34848.000, + "built_form": 248 + } + }, + { + "pk": 443, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.9985243483, + "net_built_up_area": 30492.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2033.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 12, + "gross_built_up_area": 30492.000, + "built_form": 249 + } + }, + { + "pk": 444, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.6015267176, + "net_built_up_area": 52272.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1965.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 12, + "gross_built_up_area": 52272.000, + "built_form": 250 + } + }, + { + "pk": 445, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.5035971223, + "net_built_up_area": 39204.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1668.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 12, + "gross_built_up_area": 39204.000, + "built_form": 251 + } + }, + { + "pk": 446, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.5688367129, + "net_built_up_area": 47916.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1874.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.1000000000, + "building_use_definition": 12, + "gross_built_up_area": 47916.000, + "built_form": 252 + } + }, + { + "pk": 447, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.1612903226, + "net_built_up_area": 65340.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3410.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 12, + "gross_built_up_area": 65340.000, + "built_form": 253 + } + }, + { + "pk": 448, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 30.4083769634, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1146.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 12, + "gross_built_up_area": 34848.000, + "built_form": 254 + } + }, + { + "pk": 449, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.2280000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 25, + "gross_built_up_area": 4356.000, + "built_form": 255 + } + }, + { + "pk": 450, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 25, + "gross_built_up_area": 8712.000, + "built_form": 256 + } + }, + { + "pk": 451, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.2280000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 25, + "gross_built_up_area": 4356.000, + "built_form": 257 + } + }, + { + "pk": 452, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 55.5390000000, + "net_built_up_area": 55539.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 21, + "gross_built_up_area": 65340.000, + "built_form": 258 + } + }, + { + "pk": 453, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 33.6600000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1100.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 43560.000, + "built_form": 259 + } + }, + { + "pk": 454, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.4100000000, + "net_built_up_area": 25918.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1020.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 14, + "gross_built_up_area": 30492.000, + "built_form": 260 + } + }, + { + "pk": 455, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 56.4803389831, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 590.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 14, + "gross_built_up_area": 39204.000, + "built_form": 261 + } + }, + { + "pk": 456, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 46.9343661972, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 710.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 14, + "gross_built_up_area": 39204.000, + "built_form": 262 + } + }, + { + "pk": 457, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.0623883022, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1231.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 14, + "gross_built_up_area": 34848.000, + "built_form": 263 + } + }, + { + "pk": 458, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 37.0260000000, + "net_built_up_area": 48133.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.3000000000, + "building_use_definition": 14, + "gross_built_up_area": 56628.000, + "built_form": 264 + } + }, + { + "pk": 459, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 53.7474193548, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 620.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 14, + "gross_built_up_area": 39204.000, + "built_form": 265 + } + }, + { + "pk": 460, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 25, + "gross_built_up_area": 8712.000, + "built_form": 266 + } + }, + { + "pk": 461, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 32.9120000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 25, + "gross_built_up_area": 17424.000, + "built_form": 267 + } + }, + { + "pk": 462, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 25, + "gross_built_up_area": 13068.000, + "built_form": 268 + } + }, + { + "pk": 463, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.5700000000, + "net_built_up_area": 9256.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2500000000, + "building_use_definition": 25, + "gross_built_up_area": 10890.000, + "built_form": 269 + } + }, + { + "pk": 464, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.6208000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 25, + "gross_built_up_area": 13068.000, + "built_form": 270 + } + }, + { + "pk": 465, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.3680000000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 25, + "gross_built_up_area": 26136.000, + "built_form": 271 + } + }, + { + "pk": 466, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 65.8240000000, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 25, + "gross_built_up_area": 34848.000, + "built_form": 272 + } + }, + { + "pk": 467, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 82.2800000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 25, + "gross_built_up_area": 43560.000, + "built_form": 273 + } + }, + { + "pk": 468, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 25, + "gross_built_up_area": 8712.000, + "built_form": 274 + } + }, + { + "pk": 469, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 41.1400000000, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 25, + "gross_built_up_area": 21780.000, + "built_form": 275 + } + }, + { + "pk": 470, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 25, + "gross_built_up_area": 13068.000, + "built_form": 276 + } + }, + { + "pk": 471, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.3296000000, + "net_built_up_area": 11848.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.8000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3200000000, + "building_use_definition": 25, + "gross_built_up_area": 13939.200, + "built_form": 277 + } + }, + { + "pk": 472, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9494400000, + "net_built_up_area": 2962.080, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0800000000, + "building_use_definition": 9, + "gross_built_up_area": 3484.800, + "built_form": 277 + } + }, + { + "pk": 473, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 74.0520000000, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 25, + "gross_built_up_area": 39204.000, + "built_form": 278 + } + }, + { + "pk": 474, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 9, + "gross_built_up_area": 4356.000, + "built_form": 278 + } + }, + { + "pk": 475, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.6966400000, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 0.8000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 25, + "gross_built_up_area": 10454.400, + "built_form": 279 + } + }, + { + "pk": 476, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9620800000, + "net_built_up_area": 2221.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0600000000, + "building_use_definition": 9, + "gross_built_up_area": 2613.600, + "built_form": 279 + } + }, + { + "pk": 477, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 69.1152000000, + "net_built_up_area": 25918.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 25, + "gross_built_up_area": 30492.000, + "built_form": 280 + } + }, + { + "pk": 478, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 25, + "gross_built_up_area": 13068.000, + "built_form": 281 + } + }, + { + "pk": 479, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 41.1400000000, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 25, + "gross_built_up_area": 21780.000, + "built_form": 282 + } + }, + { + "pk": 480, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 8, + "gross_built_up_area": 13068.000, + "built_form": 283 + } + }, + { + "pk": 481, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9241600000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 8, + "gross_built_up_area": 17424.000, + "built_form": 284 + } + }, + { + "pk": 482, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 8, + "gross_built_up_area": 13068.000, + "built_form": 285 + } + }, + { + "pk": 483, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 8, + "gross_built_up_area": 13068.000, + "built_form": 286 + } + }, + { + "pk": 484, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9241600000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 8, + "gross_built_up_area": 26136.000, + "built_form": 287 + } + }, + { + "pk": 485, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9494400000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 8, + "gross_built_up_area": 17424.000, + "built_form": 288 + } + }, + { + "pk": 486, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 8, + "gross_built_up_area": 17424.000, + "built_form": 289 + } + }, + { + "pk": 487, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 8, + "gross_built_up_area": 17424.000, + "built_form": 290 + } + }, + { + "pk": 488, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 8, + "gross_built_up_area": 17424.000, + "built_form": 291 + } + }, + { + "pk": 489, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 321.9652173913, + "net_built_up_area": 296208.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 920.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 348480.000, + "built_form": 292 + } + }, + { + "pk": 490, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 703.2578313253, + "net_built_up_area": 233481.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 332.000, + "percent": 1.0000000, + "efficiency": 0.8000, + "household_size": 2.500, + "floor_area_ratio": 6.7000000000, + "building_use_definition": 14, + "gross_built_up_area": 291852.000, + "built_form": 293 + } + }, + { + "pk": 491, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 240.0586813187, + "net_built_up_area": 218453.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 910.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.9000000000, + "building_use_definition": 14, + "gross_built_up_area": 257004.000, + "built_form": 294 + } + }, + { + "pk": 492, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 131.3422297297, + "net_built_up_area": 155509.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.2000000000, + "building_use_definition": 14, + "gross_built_up_area": 182952.000, + "built_form": 295 + } + }, + { + "pk": 493, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 111.0780000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 130680.000, + "built_form": 296 + } + }, + { + "pk": 494, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 93.8158783784, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 130680.000, + "built_form": 297 + } + }, + { + "pk": 495, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 52.8942857143, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1400.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 87120.000, + "built_form": 298 + } + }, + { + "pk": 496, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 130.6800000000, + "net_built_up_area": 99970.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 765.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.7000000000, + "building_use_definition": 14, + "gross_built_up_area": 117612.000, + "built_form": 299 + } + }, + { + "pk": 497, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 111.0780000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 130680.000, + "built_form": 300 + } + }, + { + "pk": 498, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 62.3209900990, + "net_built_up_area": 62944.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1010.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.7000000000, + "building_use_definition": 14, + "gross_built_up_area": 74052.000, + "built_form": 301 + } + }, + { + "pk": 499, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 78.7787234043, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 940.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 87120.000, + "built_form": 302 + } + }, + { + "pk": 500, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 222.1560000000, + "net_built_up_area": 166617.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 14, + "gross_built_up_area": 196020.000, + "built_form": 303 + } + }, + { + "pk": 501, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 103.5135483871, + "net_built_up_area": 96267.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 930.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.6000000000, + "building_use_definition": 14, + "gross_built_up_area": 113256.000, + "built_form": 304 + } + }, + { + "pk": 502, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 64.3930434783, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1150.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 14, + "gross_built_up_area": 87120.000, + "built_form": 305 + } + }, + { + "pk": 503, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.4944000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 25, + "gross_built_up_area": 17424.000, + "built_form": 306 + } + }, + { + "pk": 504, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 32.9120000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 25, + "gross_built_up_area": 17424.000, + "built_form": 307 + } + }, + { + "pk": 505, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.2594458438, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1191.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 12, + "gross_built_up_area": 34848.000, + "built_form": 308 + } + }, + { + "pk": 506, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.5035971223, + "net_built_up_area": 39204.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1668.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 12, + "gross_built_up_area": 39204.000, + "built_form": 309 + } + }, + { + "pk": 507, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.6015267176, + "net_built_up_area": 52272.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1965.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 12, + "gross_built_up_area": 52272.000, + "built_form": 310 + } + }, + { + "pk": 508, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.2320000000, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 12, + "gross_built_up_area": 34848.000, + "built_form": 311 + } + }, + { + "pk": 509, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.5688367129, + "net_built_up_area": 47916.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1874.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.1000000000, + "building_use_definition": 12, + "gross_built_up_area": 47916.000, + "built_form": 312 + } + }, + { + "pk": 510, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.9985243483, + "net_built_up_area": 30492.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2033.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 12, + "gross_built_up_area": 30492.000, + "built_form": 313 + } + }, + { + "pk": 511, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 30.4083769634, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1146.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 12, + "gross_built_up_area": 34848.000, + "built_form": 314 + } + }, + { + "pk": 512, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.1612903226, + "net_built_up_area": 65340.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3410.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 12, + "gross_built_up_area": 65340.000, + "built_form": 315 + } + }, + { + "pk": 513, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.1566265060, + "net_built_up_area": 45921.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1575.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.0542169000, + "building_use_definition": 26, + "gross_built_up_area": 45921.688, + "built_form": 316 + } + }, + { + "pk": 514, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.2409638554, + "net_built_up_area": 48414.579, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1845.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.1114458000, + "building_use_definition": 26, + "gross_built_up_area": 48414.579, + "built_form": 317 + } + }, + { + "pk": 515, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 28.1032258065, + "net_built_up_area": 49883.226, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1775.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.1451613000, + "building_use_definition": 26, + "gross_built_up_area": 49883.226, + "built_form": 318 + } + }, + { + "pk": 516, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.0057405281, + "net_built_up_area": 34457.911, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1378.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7910448000, + "building_use_definition": 26, + "gross_built_up_area": 34457.911, + "built_form": 319 + } + }, + { + "pk": 517, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.3614986050, + "net_built_up_area": 26875.601, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1548.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.6169789000, + "building_use_definition": 26, + "gross_built_up_area": 26875.601, + "built_form": 320 + } + }, + { + "pk": 518, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.9210526316, + "net_built_up_area": 30886.841, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1940.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7090643000, + "building_use_definition": 26, + "gross_built_up_area": 30886.841, + "built_form": 321 + } + }, + { + "pk": 519, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.1566265060, + "net_built_up_area": 45921.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1575.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.0542169000, + "building_use_definition": 26, + "gross_built_up_area": 45921.688, + "built_form": 322 + } + }, + { + "pk": 520, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.4240000000, + "net_built_up_area": 24393.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1400.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5600000000, + "building_use_definition": 26, + "gross_built_up_area": 24393.600, + "built_form": 323 + } + }, + { + "pk": 521, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.1566265060, + "net_built_up_area": 45921.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1575.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.0542169000, + "building_use_definition": 26, + "gross_built_up_area": 45921.688, + "built_form": 324 + } + }, + { + "pk": 522, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 98.7360000000, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 13, + "gross_built_up_area": 87120.000, + "built_form": 325 + } + }, + { + "pk": 523, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 143.1672000000, + "net_built_up_area": 107375.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.9000000000, + "building_use_definition": 9, + "gross_built_up_area": 126324.000, + "built_form": 326 + } + }, + { + "pk": 524, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 44.4312000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 20, + "gross_built_up_area": 52272.000, + "built_form": 327 + } + }, + { + "pk": 525, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 77.7546000000, + "net_built_up_area": 77754.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.1000000000, + "building_use_definition": 20, + "gross_built_up_area": 91476.000, + "built_form": 328 + } + }, + { + "pk": 526, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 103.6728000000, + "net_built_up_area": 103672.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.8000000000, + "building_use_definition": 20, + "gross_built_up_area": 121968.000, + "built_form": 329 + } + }, + { + "pk": 527, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 99.9702000000, + "net_built_up_area": 99970.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.7000000000, + "building_use_definition": 20, + "gross_built_up_area": 117612.000, + "built_form": 330 + } + }, + { + "pk": 528, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 85.1598000000, + "net_built_up_area": 85159.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.3000000000, + "building_use_definition": 20, + "gross_built_up_area": 100188.000, + "built_form": 331 + } + }, + { + "pk": 529, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.6208000000, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 20, + "gross_built_up_area": 34848.000, + "built_form": 332 + } + }, + { + "pk": 530, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 37.0260000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 20, + "gross_built_up_area": 43560.000, + "built_form": 333 + } + }, + { + "pk": 531, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 42.5799000000, + "net_built_up_area": 42579.900, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.1500000000, + "building_use_definition": 20, + "gross_built_up_area": 50094.000, + "built_form": 334 + } + }, + { + "pk": 532, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 42.5799000000, + "net_built_up_area": 42579.900, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.1500000000, + "building_use_definition": 18, + "gross_built_up_area": 50094.000, + "built_form": 334 + } + }, + { + "pk": 533, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.0294000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 20, + "gross_built_up_area": 19602.000, + "built_form": 335 + } + }, + { + "pk": 534, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.2899500000, + "net_built_up_area": 25547.940, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6900000000, + "building_use_definition": 20, + "gross_built_up_area": 30056.400, + "built_form": 336 + } + }, + { + "pk": 535, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.2676500000, + "net_built_up_area": 15921.180, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4300000000, + "building_use_definition": 20, + "gross_built_up_area": 18730.800, + "built_form": 337 + } + }, + { + "pk": 536, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.9814000000, + "net_built_up_area": 25177.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6800000000, + "building_use_definition": 19, + "gross_built_up_area": 29620.800, + "built_form": 338 + } + }, + { + "pk": 537, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4810400000, + "net_built_up_area": 444.312, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.0500000, + "efficiency": 0.3400, + "household_size": 2.500, + "floor_area_ratio": 0.0300000000, + "building_use_definition": 3, + "gross_built_up_area": 1306.800, + "built_form": 1 + } + }, + { + "pk": 538, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 35.1747000000, + "net_built_up_area": 21104.820, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5700000000, + "building_use_definition": 5, + "gross_built_up_area": 24829.200, + "built_form": 1 + } + }, + { + "pk": 539, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.7278800000, + "net_built_up_area": 518.364, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.0500000, + "efficiency": 0.3400, + "household_size": 2.500, + "floor_area_ratio": 0.0350000000, + "building_use_definition": 3, + "gross_built_up_area": 1524.600, + "built_form": 2 + } + }, + { + "pk": 540, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 41.0371500000, + "net_built_up_area": 24622.290, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6650000000, + "building_use_definition": 5, + "gross_built_up_area": 28967.400, + "built_form": 2 + } + }, + { + "pk": 541, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.0889230769, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 650.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 3 + } + }, + { + "pk": 542, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.8104000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 3, + "gross_built_up_area": 13068.000, + "built_form": 4 + } + }, + { + "pk": 543, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.6208000000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 3, + "gross_built_up_area": 26136.000, + "built_form": 5 + } + }, + { + "pk": 544, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 21780.000, + "built_form": 6 + } + }, + { + "pk": 545, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.3680000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 3, + "gross_built_up_area": 43560.000, + "built_form": 7 + } + }, + { + "pk": 546, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.3560000000, + "net_built_up_area": 12196.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2800.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2800000000, + "building_use_definition": 2, + "gross_built_up_area": 12196.800, + "built_form": 8 + } + }, + { + "pk": 547, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1780000000, + "net_built_up_area": 6534.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1500000000, + "building_use_definition": 2, + "gross_built_up_area": 6534.000, + "built_form": 9 + } + }, + { + "pk": 548, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9040000000, + "net_built_up_area": 14519.999, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3333333000, + "building_use_definition": 2, + "gross_built_up_area": 14519.999, + "built_form": 10 + } + }, + { + "pk": 549, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1780000000, + "net_built_up_area": 10890.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2500000000, + "building_use_definition": 2, + "gross_built_up_area": 10890.000, + "built_form": 11 + } + }, + { + "pk": 550, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4444444444, + "net_built_up_area": 14284.443, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3214.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3279257000, + "building_use_definition": 2, + "gross_built_up_area": 14284.443, + "built_form": 12 + } + }, + { + "pk": 551, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.1876562200, + "net_built_up_area": 20754.023, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4956.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4764468000, + "building_use_definition": 2, + "gross_built_up_area": 20754.023, + "built_form": 13 + } + }, + { + "pk": 552, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.9868073879, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1516.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 14 + } + }, + { + "pk": 553, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.1278227524, + "net_built_up_area": 56628.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2347.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.3000000000, + "building_use_definition": 2, + "gross_built_up_area": 56628.000, + "built_form": 15 + } + }, + { + "pk": 554, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 30.6760563380, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1136.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 16 + } + }, + { + "pk": 555, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 109.0725797392, + "net_built_up_area": 217490.724, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1994.000, + "percent": 0.8900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.8740000000, + "building_use_definition": 2, + "gross_built_up_area": 255871.440, + "built_form": 17 + } + }, + { + "pk": 556, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.7233263158, + "net_built_up_area": 4887.432, + "vacancy_rate": 0.000, + "square_feet_per_unit": 190.000, + "percent": 0.0500000, + "efficiency": 0.3400, + "household_size": 2.500, + "floor_area_ratio": 0.3300000000, + "building_use_definition": 3, + "gross_built_up_area": 14374.800, + "built_form": 17 + } + }, + { + "pk": 557, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 58.6491840000, + "net_built_up_area": 14662.296, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3960000000, + "building_use_definition": 5, + "gross_built_up_area": 17249.760, + "built_form": 17 + } + }, + { + "pk": 558, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 353.5163076923, + "net_built_up_area": 303316.992, + "vacancy_rate": 0.000, + "square_feet_per_unit": 858.000, + "percent": 0.6400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.1920000000, + "building_use_definition": 2, + "gross_built_up_area": 356843.520, + "built_form": 18 + } + }, + { + "pk": 559, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.9550652632, + "net_built_up_area": 9478.656, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2560000000, + "building_use_definition": 3, + "gross_built_up_area": 11151.360, + "built_form": 18 + } + }, + { + "pk": 560, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1074.2476800000, + "net_built_up_area": 161137.152, + "vacancy_rate": 0.000, + "square_feet_per_unit": 150.000, + "percent": 0.3400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.3520000000, + "building_use_definition": 5, + "gross_built_up_area": 189573.120, + "built_form": 18 + } + }, + { + "pk": 561, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 114.9443085607, + "net_built_up_area": 122185.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1063.000, + "percent": 0.5500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.3000000000, + "building_use_definition": 2, + "gross_built_up_area": 143748.000, + "built_form": 19 + } + }, + { + "pk": 562, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.0309052632, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 3, + "gross_built_up_area": 7840.800, + "built_form": 19 + } + }, + { + "pk": 563, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 373.2220800000, + "net_built_up_area": 93305.520, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.4200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.5200000000, + "building_use_definition": 5, + "gross_built_up_area": 109771.200, + "built_form": 19 + } + }, + { + "pk": 564, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 401.3627381443, + "net_built_up_area": 467186.227, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1164.000, + "percent": 0.9800000, + "efficiency": 0.7200, + "household_size": 2.500, + "floor_area_ratio": 14.8960000000, + "building_use_definition": 2, + "gross_built_up_area": 648869.760, + "built_form": 20 + } + }, + { + "pk": 565, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 27.8784000000, + "net_built_up_area": 13242.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3040000000, + "building_use_definition": 3, + "gross_built_up_area": 13242.240, + "built_form": 20 + } + }, + { + "pk": 566, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 350.5258258575, + "net_built_up_area": 531397.152, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1516.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 14.3520000000, + "building_use_definition": 2, + "gross_built_up_area": 625173.120, + "built_form": 21 + } + }, + { + "pk": 567, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 97.2656834021, + "net_built_up_area": 17325.450, + "vacancy_rate": 0.000, + "square_feet_per_unit": 178.125, + "percent": 0.0800000, + "efficiency": 0.3187, + "household_size": 2.500, + "floor_area_ratio": 1.2480000000, + "building_use_definition": 3, + "gross_built_up_area": 54362.880, + "built_form": 21 + } + }, + { + "pk": 568, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 404.2247952756, + "net_built_up_area": 410692.392, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1016.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 11.0920000000, + "building_use_definition": 2, + "gross_built_up_area": 483167.520, + "built_form": 22 + } + }, + { + "pk": 569, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 55.1818508081, + "net_built_up_area": 8737.108, + "vacancy_rate": 0.000, + "square_feet_per_unit": 158.333, + "percent": 0.0600000, + "efficiency": 0.2833, + "household_size": 2.500, + "floor_area_ratio": 0.7080000000, + "building_use_definition": 3, + "gross_built_up_area": 30840.480, + "built_form": 22 + } + }, + { + "pk": 570, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 221.0254351145, + "net_built_up_area": 289543.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1310.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 7.8200000000, + "building_use_definition": 2, + "gross_built_up_area": 340639.200, + "built_form": 23 + } + }, + { + "pk": 571, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 52.9973274947, + "net_built_up_area": 9440.149, + "vacancy_rate": 0.000, + "square_feet_per_unit": 178.125, + "percent": 0.0800000, + "efficiency": 0.3187, + "household_size": 2.500, + "floor_area_ratio": 0.6800000000, + "building_use_definition": 3, + "gross_built_up_area": 29620.800, + "built_form": 23 + } + }, + { + "pk": 572, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 160.5186000000, + "net_built_up_area": 163728.972, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1020.000, + "percent": 0.2200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.4220000000, + "building_use_definition": 2, + "gross_built_up_area": 192622.320, + "built_form": 24 + } + }, + { + "pk": 573, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 54.1252800000, + "net_built_up_area": 29768.904, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8040000000, + "building_use_definition": 3, + "gross_built_up_area": 35022.240, + "built_form": 24 + } + }, + { + "pk": 574, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2202.8988960000, + "net_built_up_area": 550724.724, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.7400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 14.8740000000, + "building_use_definition": 5, + "gross_built_up_area": 647911.440, + "built_form": 24 + } + }, + { + "pk": 575, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.2185800000, + "net_built_up_area": 6109.290, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1650000000, + "building_use_definition": 3, + "gross_built_up_area": 7187.400, + "built_form": 25 + } + }, + { + "pk": 576, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2419.2788400000, + "net_built_up_area": 604819.710, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 16.3350000000, + "building_use_definition": 5, + "gross_built_up_area": 711552.600, + "built_form": 25 + } + }, + { + "pk": 577, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2147.5080000000, + "net_built_up_area": 536877.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 14.5000000000, + "building_use_definition": 5, + "gross_built_up_area": 631620.000, + "built_form": 26 + } + }, + { + "pk": 578, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.5888400000, + "net_built_up_area": 2517.768, + "vacancy_rate": 0.000, + "square_feet_per_unit": 200.000, + "percent": 0.0500000, + "efficiency": 0.3400, + "household_size": 2.500, + "floor_area_ratio": 0.1700000000, + "building_use_definition": 3, + "gross_built_up_area": 7405.200, + "built_form": 27 + } + }, + { + "pk": 579, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 478.3759200000, + "net_built_up_area": 119593.980, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.2300000000, + "building_use_definition": 5, + "gross_built_up_area": 140698.800, + "built_form": 27 + } + }, + { + "pk": 580, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 37.1593399988, + "net_built_up_area": 6663.896, + "vacancy_rate": 0.000, + "square_feet_per_unit": 179.333, + "percent": 0.0600000, + "efficiency": 0.2833, + "household_size": 2.500, + "floor_area_ratio": 0.5400000000, + "building_use_definition": 3, + "gross_built_up_area": 23522.400, + "built_form": 28 + } + }, + { + "pk": 581, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1252.9598400000, + "net_built_up_area": 313239.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.4600000000, + "building_use_definition": 5, + "gross_built_up_area": 368517.600, + "built_form": 28 + } + }, + { + "pk": 582, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 33.0291675836, + "net_built_up_area": 6663.635, + "vacancy_rate": 0.000, + "square_feet_per_unit": 201.750, + "percent": 0.0800000, + "efficiency": 0.3187, + "household_size": 2.500, + "floor_area_ratio": 0.4800000000, + "building_use_definition": 3, + "gross_built_up_area": 20908.800, + "built_form": 29 + } + }, + { + "pk": 583, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 817.5340800000, + "net_built_up_area": 204383.520, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.5200000000, + "building_use_definition": 5, + "gross_built_up_area": 240451.200, + "built_form": 29 + } + }, + { + "pk": 584, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 81.4572000000, + "net_built_up_area": 12218.580, + "vacancy_rate": 0.000, + "square_feet_per_unit": 150.000, + "percent": 0.1000000, + "efficiency": 0.2550, + "household_size": 2.500, + "floor_area_ratio": 1.1000000000, + "building_use_definition": 3, + "gross_built_up_area": 47916.000, + "built_form": 30 + } + }, + { + "pk": 585, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1621.4774400000, + "net_built_up_area": 405369.360, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9000000, + "efficiency": 0.9400, + "household_size": 2.500, + "floor_area_ratio": 9.9000000000, + "building_use_definition": 5, + "gross_built_up_area": 431244.000, + "built_form": 30 + } + }, + { + "pk": 586, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.0698800000, + "net_built_up_area": 7034.940, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1900000000, + "building_use_definition": 3, + "gross_built_up_area": 8276.400, + "built_form": 31 + } + }, + { + "pk": 587, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2785.8362400000, + "net_built_up_area": 696459.060, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 18.8100000000, + "building_use_definition": 5, + "gross_built_up_area": 819363.600, + "built_form": 31 + } + }, + { + "pk": 588, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 569.9738546845, + "net_built_up_area": 596192.652, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1046.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 16.1020000000, + "building_use_definition": 2, + "gross_built_up_area": 701403.120, + "built_form": 32 + } + }, + { + "pk": 589, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.9754400000, + "net_built_up_area": 18438.948, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4980000000, + "building_use_definition": 3, + "gross_built_up_area": 21692.880, + "built_form": 32 + } + }, + { + "pk": 590, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 208.6251750000, + "net_built_up_area": 183590.154, + "vacancy_rate": 0.000, + "square_feet_per_unit": 880.000, + "percent": 0.9700000, + "efficiency": 0.7900, + "household_size": 2.500, + "floor_area_ratio": 5.3350000000, + "building_use_definition": 2, + "gross_built_up_area": 232392.600, + "built_form": 33 + } + }, + { + "pk": 591, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.2185800000, + "net_built_up_area": 6109.290, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1650000000, + "building_use_definition": 3, + "gross_built_up_area": 7187.400, + "built_form": 33 + } + }, + { + "pk": 592, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 232.1942989865, + "net_built_up_area": 274918.050, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 7.4250000000, + "building_use_definition": 2, + "gross_built_up_area": 323433.000, + "built_form": 34 + } + }, + { + "pk": 593, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.0490000000, + "net_built_up_area": 2776.950, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750000000, + "building_use_definition": 3, + "gross_built_up_area": 3267.000, + "built_form": 34 + } + }, + { + "pk": 594, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 215.4913200000, + "net_built_up_area": 215491.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.8200000000, + "building_use_definition": 2, + "gross_built_up_area": 253519.200, + "built_form": 35 + } + }, + { + "pk": 595, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.8862400000, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 3, + "gross_built_up_area": 7840.800, + "built_form": 35 + } + }, + { + "pk": 596, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 165.9989189189, + "net_built_up_area": 196542.720, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 0.9400000, + "efficiency": 0.8000, + "household_size": 2.500, + "floor_area_ratio": 5.6400000000, + "building_use_definition": 2, + "gross_built_up_area": 245678.400, + "built_form": 36 + } + }, + { + "pk": 597, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 28.0634316806, + "net_built_up_area": 8886.763, + "vacancy_rate": 0.000, + "square_feet_per_unit": 316.667, + "percent": 0.0600000, + "efficiency": 0.5667, + "household_size": 2.500, + "floor_area_ratio": 0.3600000000, + "building_use_definition": 3, + "gross_built_up_area": 15681.600, + "built_form": 36 + } + }, + { + "pk": 598, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 552.4432941176, + "net_built_up_area": 507142.944, + "vacancy_rate": 0.000, + "square_feet_per_unit": 918.000, + "percent": 0.9800000, + "efficiency": 0.9000, + "household_size": 2.500, + "floor_area_ratio": 12.9360000000, + "building_use_definition": 2, + "gross_built_up_area": 563492.160, + "built_form": 37 + } + }, + { + "pk": 599, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.9996800000, + "net_built_up_area": 11499.840, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2640000000, + "building_use_definition": 3, + "gross_built_up_area": 11499.840, + "built_form": 37 + } + }, + { + "pk": 600, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 396.9693538462, + "net_built_up_area": 464454.144, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1170.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 12.5440000000, + "building_use_definition": 2, + "gross_built_up_area": 546416.640, + "built_form": 38 + } + }, + { + "pk": 601, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.9550652632, + "net_built_up_area": 9478.656, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2560000000, + "building_use_definition": 3, + "gross_built_up_area": 11151.360, + "built_form": 38 + } + }, + { + "pk": 602, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 351.7768596774, + "net_built_up_area": 436203.306, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1240.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 11.7810000000, + "building_use_definition": 2, + "gross_built_up_area": 513180.360, + "built_form": 39 + } + }, + { + "pk": 603, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.7913200000, + "net_built_up_area": 4406.094, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1190000000, + "building_use_definition": 3, + "gross_built_up_area": 5183.640, + "built_form": 39 + } + }, + { + "pk": 604, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 177.7248000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 130680.000, + "built_form": 40 + } + }, + { + "pk": 605, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 118.4832000000, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 87120.000, + "built_form": 41 + } + }, + { + "pk": 606, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1151.1720000000, + "net_built_up_area": 405212.544, + "vacancy_rate": 0.000, + "square_feet_per_unit": 352.000, + "percent": 1.0000000, + "efficiency": 0.5440, + "household_size": 2.500, + "floor_area_ratio": 17.1000000000, + "building_use_definition": 3, + "gross_built_up_area": 744876.000, + "built_form": 42 + } + }, + { + "pk": 607, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 430.8480000000, + "net_built_up_area": 199051.776, + "vacancy_rate": 0.000, + "square_feet_per_unit": 462.000, + "percent": 1.0000000, + "efficiency": 0.7140, + "household_size": 2.500, + "floor_area_ratio": 6.4000000000, + "building_use_definition": 3, + "gross_built_up_area": 278784.000, + "built_form": 43 + } + }, + { + "pk": 608, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 33.6600000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 495.000, + "percent": 1.0000000, + "efficiency": 0.7650, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 21780.000, + "built_form": 44 + } + }, + { + "pk": 609, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 67.3200000000, + "net_built_up_area": 35174.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 522.500, + "percent": 1.0000000, + "efficiency": 0.8075, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 3, + "gross_built_up_area": 43560.000, + "built_form": 45 + } + }, + { + "pk": 610, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.4640000000, + "net_built_up_area": 7034.940, + "vacancy_rate": 0.000, + "square_feet_per_unit": 522.500, + "percent": 1.0000000, + "efficiency": 0.8075, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 3, + "gross_built_up_area": 8712.000, + "built_form": 46 + } + }, + { + "pk": 611, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.1240000000, + "net_built_up_area": 24622.290, + "vacancy_rate": 0.000, + "square_feet_per_unit": 522.500, + "percent": 1.0000000, + "efficiency": 0.8075, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 3, + "gross_built_up_area": 30492.000, + "built_form": 47 + } + }, + { + "pk": 612, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 16.1268800000, + "net_built_up_area": 14514.192, + "vacancy_rate": 0.000, + "square_feet_per_unit": 900.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3920000000, + "building_use_definition": 4, + "gross_built_up_area": 17075.520, + "built_form": 48 + } + }, + { + "pk": 613, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5924160000, + "net_built_up_area": 296.208, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0080000000, + "building_use_definition": 5, + "gross_built_up_area": 348.480, + "built_form": 48 + } + }, + { + "pk": 614, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.9012142857, + "net_built_up_area": 8330.850, + "vacancy_rate": 0.000, + "square_feet_per_unit": 700.000, + "percent": 0.7500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2250000000, + "building_use_definition": 4, + "gross_built_up_area": 9801.000, + "built_form": 49 + } + }, + { + "pk": 615, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.2565000000, + "net_built_up_area": 2776.950, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750000000, + "building_use_definition": 5, + "gross_built_up_area": 3267.000, + "built_form": 49 + } + }, + { + "pk": 616, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.1412500000, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 800.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 4, + "gross_built_up_area": 21780.000, + "built_form": 50 + } + }, + { + "pk": 617, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.9576100000, + "net_built_up_area": 17957.610, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4850000000, + "building_use_definition": 4, + "gross_built_up_area": 21126.600, + "built_form": 51 + } + }, + { + "pk": 618, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8513000000, + "net_built_up_area": 555.390, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0150000000, + "building_use_definition": 5, + "gross_built_up_area": 653.400, + "built_form": 51 + } + }, + { + "pk": 619, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 27.7695000000, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 320.000, + "percent": 1.0000000, + "efficiency": 0.3400, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 4, + "gross_built_up_area": 26136.000, + "built_form": 52 + } + }, + { + "pk": 620, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.9841875000, + "net_built_up_area": 17587.350, + "vacancy_rate": 0.000, + "square_feet_per_unit": 800.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4750000000, + "building_use_definition": 4, + "gross_built_up_area": 20691.000, + "built_form": 53 + } + }, + { + "pk": 621, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6447142857, + "net_built_up_area": 925.650, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0250000000, + "building_use_definition": 5, + "gross_built_up_area": 1089.000, + "built_form": 53 + } + }, + { + "pk": 622, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.2191760000, + "net_built_up_area": 10219.176, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2760000000, + "building_use_definition": 4, + "gross_built_up_area": 12022.560, + "built_form": 54 + } + }, + { + "pk": 623, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.5389257143, + "net_built_up_area": 888.624, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0240000000, + "building_use_definition": 5, + "gross_built_up_area": 1045.440, + "built_form": 54 + } + }, + { + "pk": 624, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.7360500000, + "net_built_up_area": 15736.050, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.8500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4250000000, + "building_use_definition": 4, + "gross_built_up_area": 18513.000, + "built_form": 55 + } + }, + { + "pk": 625, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9341428571, + "net_built_up_area": 2776.950, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.1500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750000000, + "building_use_definition": 5, + "gross_built_up_area": 3267.000, + "built_form": 55 + } + }, + { + "pk": 626, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.2565000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 4, + "gross_built_up_area": 13068.000, + "built_form": 56 + } + }, + { + "pk": 627, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.2280000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 900.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 4, + "gross_built_up_area": 8712.000, + "built_form": 57 + } + }, + { + "pk": 628, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.0042162162, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1850.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 4, + "gross_built_up_area": 13068.000, + "built_form": 58 + } + }, + { + "pk": 629, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4936800000, + "net_built_up_area": 370.260, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0100000000, + "building_use_definition": 3, + "gross_built_up_area": 435.600, + "built_form": 59 + } + }, + { + "pk": 630, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.1189500000, + "net_built_up_area": 18142.740, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4900000000, + "building_use_definition": 4, + "gross_built_up_area": 21344.400, + "built_form": 59 + } + }, + { + "pk": 631, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 37.0260000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 400.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 60 + } + }, + { + "pk": 632, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.8080000000, + "net_built_up_area": 16262.399, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2800.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3733333000, + "building_use_definition": 2, + "gross_built_up_area": 16262.399, + "built_form": 61 + } + }, + { + "pk": 633, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.8080000000, + "net_built_up_area": 23231.999, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5333333000, + "building_use_definition": 2, + "gross_built_up_area": 23231.999, + "built_form": 62 + } + }, + { + "pk": 634, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.5831837990, + "net_built_up_area": 26687.618, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4780.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.6126634000, + "building_use_definition": 2, + "gross_built_up_area": 26687.618, + "built_form": 63 + } + }, + { + "pk": 635, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.8080000000, + "net_built_up_area": 23231.999, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5333333000, + "building_use_definition": 2, + "gross_built_up_area": 23231.999, + "built_form": 64 + } + }, + { + "pk": 636, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 86.2488000000, + "net_built_up_area": 43124.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 43560.000, + "built_form": 65 + } + }, + { + "pk": 637, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.8080000000, + "net_built_up_area": 4356.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 3, + "gross_built_up_area": 4356.000, + "built_form": 66 + } + }, + { + "pk": 638, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.8736000000, + "net_built_up_area": 5553.900, + "vacancy_rate": 0.000, + "square_feet_per_unit": 562.500, + "percent": 1.0000000, + "efficiency": 0.6375, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 3, + "gross_built_up_area": 8712.000, + "built_form": 67 + } + }, + { + "pk": 639, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.8104000000, + "net_built_up_area": 7220.070, + "vacancy_rate": 0.000, + "square_feet_per_unit": 487.500, + "percent": 1.0000000, + "efficiency": 0.5525, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 3, + "gross_built_up_area": 13068.000, + "built_form": 68 + } + }, + { + "pk": 640, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.0698800000, + "net_built_up_area": 10552.410, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2850000000, + "building_use_definition": 3, + "gross_built_up_area": 12414.600, + "built_form": 69 + } + }, + { + "pk": 641, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1107800000, + "net_built_up_area": 555.390, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0150000000, + "building_use_definition": 5, + "gross_built_up_area": 653.400, + "built_form": 69 + } + }, + { + "pk": 642, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.9250539996, + "net_built_up_area": 6900.035, + "vacancy_rate": 0.000, + "square_feet_per_unit": 631.579, + "percent": 0.9500000, + "efficiency": 0.8337, + "household_size": 2.500, + "floor_area_ratio": 0.1900000000, + "building_use_definition": 3, + "gross_built_up_area": 8276.400, + "built_form": 70 + } + }, + { + "pk": 643, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8624880000, + "net_built_up_area": 431.244, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0100000000, + "building_use_definition": 5, + "gross_built_up_area": 435.600, + "built_form": 70 + } + }, + { + "pk": 644, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.6160000000, + "net_built_up_area": 2613.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 225.000, + "percent": 1.0000000, + "efficiency": 0.3000, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 3, + "gross_built_up_area": 8712.000, + "built_form": 71 + } + }, + { + "pk": 645, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 162.6258857143, + "net_built_up_area": 125221.932, + "vacancy_rate": 0.000, + "square_feet_per_unit": 770.000, + "percent": 0.8900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.3820000000, + "building_use_definition": 2, + "gross_built_up_area": 147319.920, + "built_form": 72 + } + }, + { + "pk": 646, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 32.5828800000, + "net_built_up_area": 15476.868, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4180000000, + "building_use_definition": 3, + "gross_built_up_area": 18208.080, + "built_form": 72 + } + }, + { + "pk": 647, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 159.6410869565, + "net_built_up_area": 110152.350, + "vacancy_rate": 0.000, + "square_feet_per_unit": 690.000, + "percent": 0.8500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.9750000000, + "building_use_definition": 2, + "gross_built_up_area": 129591.000, + "built_form": 73 + } + }, + { + "pk": 648, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.9234736842, + "net_built_up_area": 19438.650, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5250000000, + "building_use_definition": 3, + "gross_built_up_area": 22869.000, + "built_form": 73 + } + }, + { + "pk": 649, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 197.2251600000, + "net_built_up_area": 118335.096, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.1960000000, + "building_use_definition": 2, + "gross_built_up_area": 139217.760, + "built_form": 74 + } + }, + { + "pk": 650, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.9016926316, + "net_built_up_area": 7553.304, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2040000000, + "building_use_definition": 3, + "gross_built_up_area": 8886.240, + "built_form": 74 + } + }, + { + "pk": 651, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.5200000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 935.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 2, + "gross_built_up_area": 52272.000, + "built_form": 75 + } + }, + { + "pk": 652, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 46.7696842105, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 3, + "gross_built_up_area": 26136.000, + "built_form": 75 + } + }, + { + "pk": 653, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 148.1040000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 52272.000, + "built_form": 75 + } + }, + { + "pk": 654, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 96.3568192771, + "net_built_up_area": 79976.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 830.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.1600000000, + "building_use_definition": 2, + "gross_built_up_area": 94089.600, + "built_form": 76 + } + }, + { + "pk": 655, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.7078736842, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 3, + "gross_built_up_area": 10454.400, + "built_form": 76 + } + }, + { + "pk": 656, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 31.0590720000, + "net_built_up_area": 34164.979, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1100.000, + "percent": 0.7600000, + "efficiency": 0.8600, + "household_size": 2.500, + "floor_area_ratio": 0.9120000000, + "building_use_definition": 2, + "gross_built_up_area": 39726.720, + "built_form": 77 + } + }, + { + "pk": 657, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.6966400000, + "net_built_up_area": 10663.488, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.2400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2880000000, + "building_use_definition": 3, + "gross_built_up_area": 12545.280, + "built_form": 77 + } + }, + { + "pk": 658, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 177.7248000000, + "net_built_up_area": 133293.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.6000000000, + "building_use_definition": 2, + "gross_built_up_area": 156816.000, + "built_form": 78 + } + }, + { + "pk": 659, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 31.1797894737, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 237.500, + "percent": 0.1000000, + "efficiency": 0.4250, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 3, + "gross_built_up_area": 17424.000, + "built_form": 78 + } + }, + { + "pk": 660, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 52.2474129821, + "net_built_up_area": 55539.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1063.000, + "percent": 0.7500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 2, + "gross_built_up_area": 65340.000, + "built_form": 79 + } + }, + { + "pk": 661, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 38.9747368421, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 21780.000, + "built_form": 79 + } + }, + { + "pk": 662, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 153.2358778626, + "net_built_up_area": 120443.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 786.000, + "percent": 0.7000000, + "efficiency": 0.7900, + "household_size": 2.500, + "floor_area_ratio": 3.5000000000, + "building_use_definition": 2, + "gross_built_up_area": 152460.000, + "built_form": 80 + } + }, + { + "pk": 663, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 98.6040000000, + "net_built_up_area": 54232.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.3000000, + "efficiency": 0.8300, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 65340.000, + "built_form": 80 + } + }, + { + "pk": 664, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 279.3780000000, + "net_built_up_area": 175170.006, + "vacancy_rate": 0.000, + "square_feet_per_unit": 627.000, + "percent": 0.8300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.7310000000, + "building_use_definition": 2, + "gross_built_up_area": 206082.360, + "built_form": 81 + } + }, + { + "pk": 665, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 75.5330400000, + "net_built_up_area": 35878.194, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9690000000, + "building_use_definition": 3, + "gross_built_up_area": 42209.640, + "built_form": 81 + } + }, + { + "pk": 666, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 166.6170000000, + "net_built_up_area": 99970.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.7000000000, + "building_use_definition": 2, + "gross_built_up_area": 117612.000, + "built_form": 82 + } + }, + { + "pk": 667, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.3848421053, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 3, + "gross_built_up_area": 13068.000, + "built_form": 82 + } + }, + { + "pk": 668, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 139.8587142857, + "net_built_up_area": 99859.122, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.000, + "percent": 0.9300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.6970000000, + "building_use_definition": 2, + "gross_built_up_area": 117481.320, + "built_form": 83 + } + }, + { + "pk": 669, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.8232527696, + "net_built_up_area": 4294.890, + "vacancy_rate": 0.000, + "square_feet_per_unit": 271.429, + "percent": 0.0700000, + "efficiency": 0.4857, + "household_size": 2.500, + "floor_area_ratio": 0.2030000000, + "building_use_definition": 3, + "gross_built_up_area": 8842.680, + "built_form": 83 + } + }, + { + "pk": 670, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 194.0851255814, + "net_built_up_area": 166913.208, + "vacancy_rate": 0.000, + "square_feet_per_unit": 860.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5080000000, + "building_use_definition": 2, + "gross_built_up_area": 196368.480, + "built_form": 84 + } + }, + { + "pk": 671, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.1713515789, + "net_built_up_area": 3406.392, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0920000000, + "building_use_definition": 3, + "gross_built_up_area": 4007.520, + "built_form": 84 + } + }, + { + "pk": 672, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 191.6945586735, + "net_built_up_area": 150288.534, + "vacancy_rate": 0.000, + "square_feet_per_unit": 784.000, + "percent": 0.9900000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.0590000000, + "building_use_definition": 2, + "gross_built_up_area": 176810.040, + "built_form": 85 + } + }, + { + "pk": 673, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.1959284211, + "net_built_up_area": 1518.066, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0410000000, + "building_use_definition": 3, + "gross_built_up_area": 1785.960, + "built_form": 85 + } + }, + { + "pk": 674, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 149.7680898876, + "net_built_up_area": 106634.880, + "vacancy_rate": 0.000, + "square_feet_per_unit": 712.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.8800000000, + "building_use_definition": 2, + "gross_built_up_area": 125452.800, + "built_form": 86 + } + }, + { + "pk": 675, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.5424000000, + "net_built_up_area": 4739.328, + "vacancy_rate": 0.000, + "square_feet_per_unit": 220.000, + "percent": 0.1000000, + "efficiency": 0.3400, + "household_size": 2.500, + "floor_area_ratio": 0.3200000000, + "building_use_definition": 3, + "gross_built_up_area": 13939.200, + "built_form": 86 + } + }, + { + "pk": 676, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 275.2862453532, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 269.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 87120.000, + "built_form": 87 + } + }, + { + "pk": 677, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 476.0485714286, + "net_built_up_area": 166617.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 5, + "gross_built_up_area": 196020.000, + "built_form": 88 + } + }, + { + "pk": 678, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 488.4280851064, + "net_built_up_area": 114780.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 235.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.1000000000, + "building_use_definition": 5, + "gross_built_up_area": 135036.000, + "built_form": 89 + } + }, + { + "pk": 679, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 71.0899200000, + "net_built_up_area": 71089.920, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.9200000000, + "building_use_definition": 2, + "gross_built_up_area": 83635.200, + "built_form": 90 + } + }, + { + "pk": 680, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 71.0899200000, + "net_built_up_area": 17772.480, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.2000000, + "efficiency": 0.4250, + "household_size": 2.500, + "floor_area_ratio": 0.9600000000, + "building_use_definition": 3, + "gross_built_up_area": 41817.600, + "built_form": 90 + } + }, + { + "pk": 681, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 341.7784615385, + "net_built_up_area": 71089.920, + "vacancy_rate": 0.000, + "square_feet_per_unit": 208.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.9200000000, + "building_use_definition": 5, + "gross_built_up_area": 83635.200, + "built_form": 90 + } + }, + { + "pk": 682, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 128.3568000000, + "net_built_up_area": 48133.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 56628.000, + "built_form": 91 + } + }, + { + "pk": 683, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.7345600000, + "net_built_up_area": 10367.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2800000000, + "building_use_definition": 3, + "gross_built_up_area": 12196.800, + "built_form": 92 + } + }, + { + "pk": 684, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 266.5872000000, + "net_built_up_area": 93305.520, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.5200000000, + "building_use_definition": 5, + "gross_built_up_area": 109771.200, + "built_form": 92 + } + }, + { + "pk": 685, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 105.7885714286, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 43560.000, + "built_form": 93 + } + }, + { + "pk": 686, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 123.4200000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 43560.000, + "built_form": 94 + } + }, + { + "pk": 687, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 133.9988571429, + "net_built_up_area": 70349.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.9000000000, + "building_use_definition": 5, + "gross_built_up_area": 82764.000, + "built_form": 95 + } + }, + { + "pk": 688, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 56.4205714286, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 3, + "gross_built_up_area": 34848.000, + "built_form": 96 + } + }, + { + "pk": 689, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 56.4205714286, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 3, + "gross_built_up_area": 34848.000, + "built_form": 97 + } + }, + { + "pk": 690, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 91.6834285714, + "net_built_up_area": 48133.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.3000000000, + "building_use_definition": 3, + "gross_built_up_area": 56628.000, + "built_form": 98 + } + }, + { + "pk": 691, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 63.4731428571, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 3, + "gross_built_up_area": 39204.000, + "built_form": 99 + } + }, + { + "pk": 692, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 77.5782857143, + "net_built_up_area": 40728.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.1000000000, + "building_use_definition": 3, + "gross_built_up_area": 47916.000, + "built_form": 100 + } + }, + { + "pk": 693, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 28.2102857143, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 3, + "gross_built_up_area": 17424.000, + "built_form": 101 + } + }, + { + "pk": 694, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 63.4731428571, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 5, + "gross_built_up_area": 26136.000, + "built_form": 101 + } + }, + { + "pk": 695, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 82.1185757746, + "net_built_up_area": 58304.189, + "vacancy_rate": 0.000, + "square_feet_per_unit": 710.000, + "percent": 0.7800000, + "efficiency": 0.7800, + "household_size": 2.500, + "floor_area_ratio": 1.7160000000, + "building_use_definition": 2, + "gross_built_up_area": 74748.960, + "built_form": 102 + } + }, + { + "pk": 696, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 43.5716160000, + "net_built_up_area": 19607.227, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.2200000, + "efficiency": 0.9300, + "household_size": 2.500, + "floor_area_ratio": 0.4840000000, + "building_use_definition": 3, + "gross_built_up_area": 21083.040, + "built_form": 102 + } + }, + { + "pk": 697, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 135.5358305630, + "net_built_up_area": 50554.865, + "vacancy_rate": 0.000, + "square_feet_per_unit": 373.000, + "percent": 0.6900000, + "efficiency": 0.5800, + "household_size": 2.500, + "floor_area_ratio": 2.0010000000, + "building_use_definition": 2, + "gross_built_up_area": 87163.560, + "built_form": 103 + } + }, + { + "pk": 698, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 87.0232000000, + "net_built_up_area": 39160.440, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.3100000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8990000000, + "building_use_definition": 3, + "gross_built_up_area": 39160.440, + "built_form": 103 + } + }, + { + "pk": 699, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 107.1282337079, + "net_built_up_area": 47672.064, + "vacancy_rate": 0.000, + "square_feet_per_unit": 445.000, + "percent": 0.4800000, + "efficiency": 0.9500, + "household_size": 2.500, + "floor_area_ratio": 1.1520000000, + "building_use_definition": 3, + "gross_built_up_area": 50181.120, + "built_form": 104 + } + }, + { + "pk": 700, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 172.1491200000, + "net_built_up_area": 51644.736, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.5200000, + "efficiency": 0.9500, + "household_size": 2.500, + "floor_area_ratio": 1.2480000000, + "building_use_definition": 5, + "gross_built_up_area": 54362.880, + "built_form": 104 + } + }, + { + "pk": 701, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.4320000000, + "net_built_up_area": 36285.480, + "vacancy_rate": 0.000, + "square_feet_per_unit": 765.000, + "percent": 0.7000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9800000000, + "building_use_definition": 2, + "gross_built_up_area": 42688.800, + "built_form": 105 + } + }, + { + "pk": 702, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.9182000000, + "net_built_up_area": 12959.100, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3500000000, + "building_use_definition": 3, + "gross_built_up_area": 15246.000, + "built_form": 105 + } + }, + { + "pk": 703, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.4052000000, + "net_built_up_area": 2591.820, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0700000000, + "building_use_definition": 5, + "gross_built_up_area": 3049.200, + "built_form": 105 + } + }, + { + "pk": 704, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.9580000000, + "net_built_up_area": 18327.870, + "vacancy_rate": 0.000, + "square_feet_per_unit": 765.000, + "percent": 0.4500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4950000000, + "building_use_definition": 2, + "gross_built_up_area": 21562.200, + "built_form": 106 + } + }, + { + "pk": 705, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 30.0084037066, + "net_built_up_area": 10181.431, + "vacancy_rate": 0.000, + "square_feet_per_unit": 339.286, + "percent": 0.3500000, + "efficiency": 0.6071, + "household_size": 2.500, + "floor_area_ratio": 0.3850000000, + "building_use_definition": 3, + "gross_built_up_area": 16770.600, + "built_form": 106 + } + }, + { + "pk": 706, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.2734857143, + "net_built_up_area": 8145.720, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2200000000, + "building_use_definition": 5, + "gross_built_up_area": 9583.200, + "built_form": 106 + } + }, + { + "pk": 707, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 12959.100, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1190.000, + "percent": 0.7000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3500000000, + "building_use_definition": 2, + "gross_built_up_area": 15246.000, + "built_form": 107 + } + }, + { + "pk": 708, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.7436842105, + "net_built_up_area": 4628.250, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1250000000, + "building_use_definition": 3, + "gross_built_up_area": 5445.000, + "built_form": 107 + } + }, + { + "pk": 709, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6447142857, + "net_built_up_area": 925.650, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0250000000, + "building_use_definition": 5, + "gross_built_up_area": 1089.000, + "built_form": 107 + } + }, + { + "pk": 710, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 83.9749680000, + "net_built_up_area": 83974.968, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.8100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.2680000000, + "building_use_definition": 2, + "gross_built_up_area": 98794.080, + "built_form": 108 + } + }, + { + "pk": 711, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.9526720000, + "net_built_up_area": 22478.702, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.1900000, + "efficiency": 0.9700, + "household_size": 2.500, + "floor_area_ratio": 0.5320000000, + "building_use_definition": 3, + "gross_built_up_area": 23173.920, + "built_form": 108 + } + }, + { + "pk": 712, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 55.5668926431, + "net_built_up_area": 61179.149, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1101.000, + "percent": 0.7700000, + "efficiency": 0.9600, + "household_size": 2.500, + "floor_area_ratio": 1.4630000000, + "building_use_definition": 2, + "gross_built_up_area": 63728.280, + "built_form": 109 + } + }, + { + "pk": 713, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 38.9672385882, + "net_built_up_area": 16561.076, + "vacancy_rate": 0.000, + "square_feet_per_unit": 425.000, + "percent": 0.2300000, + "efficiency": 0.8700, + "household_size": 2.500, + "floor_area_ratio": 0.4370000000, + "building_use_definition": 3, + "gross_built_up_area": 19035.720, + "built_form": 109 + } + }, + { + "pk": 714, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.1461147695, + "net_built_up_area": 11848.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1063.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3200000000, + "building_use_definition": 2, + "gross_built_up_area": 13939.200, + "built_form": 110 + } + }, + { + "pk": 715, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.6166319825, + "net_built_up_area": 12545.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 316.667, + "percent": 0.6000000, + "efficiency": 0.6000, + "household_size": 2.500, + "floor_area_ratio": 0.4800000000, + "building_use_definition": 3, + "gross_built_up_area": 20908.800, + "built_form": 110 + } + }, + { + "pk": 716, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 74.0520000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 2, + "gross_built_up_area": 52272.000, + "built_form": 111 + } + }, + { + "pk": 717, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 59.2416000000, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 3, + "gross_built_up_area": 34848.000, + "built_form": 111 + } + }, + { + "pk": 718, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.8059717084, + "net_built_up_area": 36581.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 919.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9880000000, + "building_use_definition": 2, + "gross_built_up_area": 43037.280, + "built_form": 112 + } + }, + { + "pk": 719, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 67.5354240000, + "net_built_up_area": 33767.712, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9120000000, + "building_use_definition": 3, + "gross_built_up_area": 39726.720, + "built_form": 112 + } + }, + { + "pk": 720, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.8059717084, + "net_built_up_area": 36581.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 919.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9880000000, + "building_use_definition": 2, + "gross_built_up_area": 43037.280, + "built_form": 113 + } + }, + { + "pk": 721, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 67.5354240000, + "net_built_up_area": 33767.712, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9120000000, + "building_use_definition": 3, + "gross_built_up_area": 39726.720, + "built_form": 113 + } + }, + { + "pk": 722, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.8059717084, + "net_built_up_area": 36581.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 919.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9880000000, + "building_use_definition": 2, + "gross_built_up_area": 43037.280, + "built_form": 114 + } + }, + { + "pk": 723, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 64.3194514286, + "net_built_up_area": 33767.712, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9120000000, + "building_use_definition": 3, + "gross_built_up_area": 39726.720, + "built_form": 114 + } + }, + { + "pk": 724, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 70.7850000000, + "net_built_up_area": 38507.040, + "vacancy_rate": 0.000, + "square_feet_per_unit": 544.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0400000000, + "building_use_definition": 2, + "gross_built_up_area": 45302.400, + "built_form": 115 + } + }, + { + "pk": 725, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 67.7046857143, + "net_built_up_area": 35544.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9600000000, + "building_use_definition": 3, + "gross_built_up_area": 41817.600, + "built_form": 115 + } + }, + { + "pk": 726, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 43.2056549790, + "net_built_up_area": 30805.632, + "vacancy_rate": 0.000, + "square_feet_per_unit": 713.000, + "percent": 0.5200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8320000000, + "building_use_definition": 2, + "gross_built_up_area": 36241.920, + "built_form": 116 + } + }, + { + "pk": 727, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 53.1513420561, + "net_built_up_area": 28435.968, + "vacancy_rate": 0.000, + "square_feet_per_unit": 535.000, + "percent": 0.4800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7680000000, + "building_use_definition": 3, + "gross_built_up_area": 33454.080, + "built_form": 116 + } + }, + { + "pk": 728, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 46.1823120000, + "net_built_up_area": 34636.734, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9500000, + "efficiency": 0.9300, + "household_size": 2.500, + "floor_area_ratio": 0.8550000000, + "building_use_definition": 3, + "gross_built_up_area": 37243.800, + "built_form": 117 + } + }, + { + "pk": 729, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.3323400000, + "net_built_up_area": 1666.170, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0450000000, + "building_use_definition": 5, + "gross_built_up_area": 1960.200, + "built_form": 117 + } + }, + { + "pk": 730, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.9996800000, + "net_built_up_area": 12937.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 562.500, + "percent": 0.8000000, + "efficiency": 0.7425, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 3, + "gross_built_up_area": 17424.000, + "built_form": 118 + } + }, + { + "pk": 731, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.4052000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 5, + "gross_built_up_area": 4356.000, + "built_form": 118 + } + }, + { + "pk": 732, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.3672800000, + "net_built_up_area": 7775.460, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.7000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2100000000, + "building_use_definition": 3, + "gross_built_up_area": 9147.600, + "built_form": 119 + } + }, + { + "pk": 733, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.6646800000, + "net_built_up_area": 3332.340, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.3000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0900000000, + "building_use_definition": 5, + "gross_built_up_area": 3920.400, + "built_form": 119 + } + }, + { + "pk": 734, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.6646800000, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 3, + "gross_built_up_area": 7840.800, + "built_form": 120 + } + }, + { + "pk": 735, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.8862400000, + "net_built_up_area": 4443.120, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1200000000, + "building_use_definition": 5, + "gross_built_up_area": 5227.200, + "built_form": 120 + } + }, + { + "pk": 736, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.8104000000, + "net_built_up_area": 7775.460, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 1.0000000, + "efficiency": 0.5950, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 3, + "gross_built_up_area": 13068.000, + "built_form": 121 + } + }, + { + "pk": 737, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.4441600000, + "net_built_up_area": 9199.872, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.5280, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 3, + "gross_built_up_area": 17424.000, + "built_form": 122 + } + }, + { + "pk": 738, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9200000000, + "net_built_up_area": 19800.002, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4545455000, + "building_use_definition": 2, + "gross_built_up_area": 19800.002, + "built_form": 123 + } + }, + { + "pk": 739, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.6800000000, + "net_built_up_area": 17569.199, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1815.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4033333000, + "building_use_definition": 2, + "gross_built_up_area": 17569.199, + "built_form": 124 + } + }, + { + "pk": 740, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9200000000, + "net_built_up_area": 17424.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2200.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 2, + "gross_built_up_area": 17424.000, + "built_form": 125 + } + }, + { + "pk": 741, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.7120000000, + "net_built_up_area": 21780.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 2, + "gross_built_up_area": 21780.000, + "built_form": 126 + } + }, + { + "pk": 742, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.8000000000, + "net_built_up_area": 11739.198, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1334.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2694949000, + "building_use_definition": 2, + "gross_built_up_area": 11739.198, + "built_form": 127 + } + }, + { + "pk": 743, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9200000000, + "net_built_up_area": 19007.998, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2400.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4363636000, + "building_use_definition": 2, + "gross_built_up_area": 19007.998, + "built_form": 128 + } + }, + { + "pk": 744, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9200000000, + "net_built_up_area": 15127.199, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1910.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3472727000, + "building_use_definition": 2, + "gross_built_up_area": 15127.199, + "built_form": 129 + } + }, + { + "pk": 745, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 83.5958607714, + "net_built_up_area": 88862.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1063.000, + "percent": 0.8000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.4000000000, + "building_use_definition": 2, + "gross_built_up_area": 104544.000, + "built_form": 130 + } + }, + { + "pk": 746, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 46.7696842105, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 3, + "gross_built_up_area": 26136.000, + "built_form": 130 + } + }, + { + "pk": 747, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 249.7794984227, + "net_built_up_area": 316720.404, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1268.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.5540000000, + "building_use_definition": 2, + "gross_built_up_area": 372612.240, + "built_form": 131 + } + }, + { + "pk": 748, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 42.5604126316, + "net_built_up_area": 20216.196, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5460000000, + "building_use_definition": 3, + "gross_built_up_area": 23783.760, + "built_form": 131 + } + }, + { + "pk": 749, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 158.7266317241, + "net_built_up_area": 230153.616, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1450.000, + "percent": 0.8400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 6.2160000000, + "building_use_definition": 2, + "gross_built_up_area": 270768.960, + "built_form": 132 + } + }, + { + "pk": 750, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 34.6095663158, + "net_built_up_area": 16439.544, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4440000000, + "building_use_definition": 3, + "gross_built_up_area": 19340.640, + "built_form": 132 + } + }, + { + "pk": 751, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 91.3308000000, + "net_built_up_area": 27399.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7400000000, + "building_use_definition": 5, + "gross_built_up_area": 32234.400, + "built_form": 132 + } + }, + { + "pk": 752, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 136.2220200000, + "net_built_up_area": 149844.222, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1100.000, + "percent": 0.5700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.0470000000, + "building_use_definition": 2, + "gross_built_up_area": 176287.320, + "built_form": 133 + } + }, + { + "pk": 753, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 57.3566400000, + "net_built_up_area": 15773.076, + "vacancy_rate": 0.000, + "square_feet_per_unit": 275.000, + "percent": 0.1200000, + "efficiency": 0.4250, + "household_size": 2.500, + "floor_area_ratio": 0.8520000000, + "building_use_definition": 3, + "gross_built_up_area": 37113.120, + "built_form": 133 + } + }, + { + "pk": 754, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 271.6474200000, + "net_built_up_area": 81494.226, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.3100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.2010000000, + "building_use_definition": 5, + "gross_built_up_area": 95875.560, + "built_form": 133 + } + }, + { + "pk": 755, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 34.4107806691, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 21780.000, + "built_form": 134 + } + }, + { + "pk": 756, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 619.3940520446, + "net_built_up_area": 166617.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 269.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 5, + "gross_built_up_area": 196020.000, + "built_form": 134 + } + }, + { + "pk": 757, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1135.4640000000, + "net_built_up_area": 170319.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 150.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.6000000000, + "building_use_definition": 5, + "gross_built_up_area": 200376.000, + "built_form": 135 + } + }, + { + "pk": 758, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 34.0639200000, + "net_built_up_area": 10219.176, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.1000000, + "efficiency": 0.5100, + "household_size": 2.500, + "floor_area_ratio": 0.4600000000, + "building_use_definition": 3, + "gross_built_up_area": 20037.600, + "built_form": 136 + } + }, + { + "pk": 759, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1021.9176000000, + "net_built_up_area": 153287.640, + "vacancy_rate": 0.000, + "square_feet_per_unit": 150.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.1400000000, + "building_use_definition": 5, + "gross_built_up_area": 180338.400, + "built_form": 136 + } + }, + { + "pk": 760, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 34.4107806691, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 322.800, + "percent": 0.1000000, + "efficiency": 0.5100, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 21780.000, + "built_form": 137 + } + }, + { + "pk": 761, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 619.3940520446, + "net_built_up_area": 166617.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 269.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 5, + "gross_built_up_area": 196020.000, + "built_form": 137 + } + }, + { + "pk": 762, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.1078000000, + "net_built_up_area": 5553.900, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1500000000, + "building_use_definition": 3, + "gross_built_up_area": 6534.000, + "built_form": 138 + } + }, + { + "pk": 763, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 301.4974285714, + "net_built_up_area": 105524.100, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.8500000000, + "building_use_definition": 5, + "gross_built_up_area": 124146.000, + "built_form": 138 + } + }, + { + "pk": 764, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 592.4160000000, + "net_built_up_area": 207345.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.6000000000, + "building_use_definition": 5, + "gross_built_up_area": 243936.000, + "built_form": 139 + } + }, + { + "pk": 765, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.2944200000, + "net_built_up_area": 3147.210, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0850000000, + "building_use_definition": 3, + "gross_built_up_area": 3702.600, + "built_form": 140 + } + }, + { + "pk": 766, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 199.3233000000, + "net_built_up_area": 59796.990, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.6150000000, + "building_use_definition": 5, + "gross_built_up_area": 70349.400, + "built_form": 140 + } + }, + { + "pk": 767, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6928000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 8712.000, + "built_form": 141 + } + }, + { + "pk": 768, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8513000000, + "net_built_up_area": 9256.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2500000000, + "building_use_definition": 5, + "gross_built_up_area": 10890.000, + "built_form": 142 + } + }, + { + "pk": 769, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.9747200000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 8712.000, + "built_form": 143 + } + }, + { + "pk": 770, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9670714286, + "net_built_up_area": 2776.950, + "vacancy_rate": 0.000, + "square_feet_per_unit": 700.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750000000, + "building_use_definition": 4, + "gross_built_up_area": 3267.000, + "built_form": 144 + } + }, + { + "pk": 771, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 27.7695000000, + "net_built_up_area": 8330.850, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.7500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2250000000, + "building_use_definition": 5, + "gross_built_up_area": 9801.000, + "built_form": 144 + } + }, + { + "pk": 772, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 86.3940000000, + "net_built_up_area": 25918.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 5, + "gross_built_up_area": 30492.000, + "built_form": 145 + } + }, + { + "pk": 773, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 42.3154285714, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 146 + } + }, + { + "pk": 774, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 275.2862453532, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 269.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 87120.000, + "built_form": 147 + } + }, + { + "pk": 775, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.9182000000, + "net_built_up_area": 12959.100, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3500000000, + "building_use_definition": 5, + "gross_built_up_area": 15246.000, + "built_form": 148 + } + }, + { + "pk": 776, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.7026000000, + "net_built_up_area": 1851.300, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0500000000, + "building_use_definition": 3, + "gross_built_up_area": 2178.000, + "built_form": 149 + } + }, + { + "pk": 777, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 117.2490000000, + "net_built_up_area": 35174.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9500000000, + "building_use_definition": 5, + "gross_built_up_area": 41382.000, + "built_form": 149 + } + }, + { + "pk": 778, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 123.4200000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 43560.000, + "built_form": 150 + } + }, + { + "pk": 779, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.3680000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 151 + } + }, + { + "pk": 780, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.8862400000, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 4, + "gross_built_up_area": 10454.400, + "built_form": 152 + } + }, + { + "pk": 781, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.7977600000, + "net_built_up_area": 5924.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1600000000, + "building_use_definition": 5, + "gross_built_up_area": 6969.600, + "built_form": 152 + } + }, + { + "pk": 782, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 4443.120, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.4000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1200000000, + "building_use_definition": 4, + "gross_built_up_area": 5227.200, + "built_form": 153 + } + }, + { + "pk": 783, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.7724800000, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 5, + "gross_built_up_area": 7840.800, + "built_form": 153 + } + }, + { + "pk": 784, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.2215600000, + "net_built_up_area": 2221.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0600000000, + "building_use_definition": 4, + "gross_built_up_area": 2613.600, + "built_form": 154 + } + }, + { + "pk": 785, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.8000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 5, + "gross_built_up_area": 10454.400, + "built_form": 154 + } + }, + { + "pk": 786, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 16.4560000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 8712.000, + "built_form": 155 + } + }, + { + "pk": 787, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.0492000000, + "net_built_up_area": 1524.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 3.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 152460.000, + "built_form": 156 + } + }, + { + "pk": 788, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9204000000, + "net_built_up_area": 1960.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 196020.000, + "built_form": 157 + } + }, + { + "pk": 789, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1780000000, + "net_built_up_area": 1089.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 2.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 108900.000, + "built_form": 158 + } + }, + { + "pk": 790, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9204000000, + "net_built_up_area": 1960.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 196020.000, + "built_form": 159 + } + }, + { + "pk": 791, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.7916000000, + "net_built_up_area": 2395.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 5.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 239580.000, + "built_form": 160 + } + }, + { + "pk": 792, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.3420000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 2, + "gross_built_up_area": 17424.000, + "built_form": 161 + } + }, + { + "pk": 793, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.8104000000, + "net_built_up_area": 2962.080, + "vacancy_rate": 0.000, + "square_feet_per_unit": 200.000, + "percent": 0.5000000, + "efficiency": 0.1700, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 3, + "gross_built_up_area": 17424.000, + "built_form": 161 + } + }, + { + "pk": 794, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1500.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 162 + } + }, + { + "pk": 795, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.6208000000, + "net_built_up_area": 17772.480, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.5000000, + "efficiency": 0.5100, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 3, + "gross_built_up_area": 34848.000, + "built_form": 162 + } + }, + { + "pk": 796, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1800.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 2, + "gross_built_up_area": 52272.000, + "built_form": 163 + } + }, + { + "pk": 797, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 44.4312000000, + "net_built_up_area": 12440.736, + "vacancy_rate": 0.000, + "square_feet_per_unit": 280.000, + "percent": 0.5000000, + "efficiency": 0.2380, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 3, + "gross_built_up_area": 52272.000, + "built_form": 163 + } + }, + { + "pk": 798, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.9580000000, + "net_built_up_area": 2395.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 100.000, + "percent": 0.5000000, + "efficiency": 0.0200, + "household_size": 2.500, + "floor_area_ratio": 2.7500000000, + "building_use_definition": 3, + "gross_built_up_area": 119790.000, + "built_form": 164 + } + }, + { + "pk": 799, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 45.6342857143, + "net_built_up_area": 23958.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.5000000, + "efficiency": 0.2000, + "household_size": 2.500, + "floor_area_ratio": 2.7500000000, + "building_use_definition": 5, + "gross_built_up_area": 119790.000, + "built_form": 164 + } + }, + { + "pk": 800, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.6396000000, + "net_built_up_area": 5945.940, + "vacancy_rate": 0.000, + "square_feet_per_unit": 150.000, + "percent": 1.0000000, + "efficiency": 0.0210, + "household_size": 2.500, + "floor_area_ratio": 6.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 283140.000, + "built_form": 165 + } + }, + { + "pk": 801, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.7800000000, + "net_built_up_area": 3811.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 175.000, + "percent": 1.0000000, + "efficiency": 0.0350, + "household_size": 2.500, + "floor_area_ratio": 2.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 108900.000, + "built_form": 166 + } + }, + { + "pk": 802, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 562.500, + "percent": 1.0000000, + "efficiency": 0.6375, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 3, + "gross_built_up_area": 17424.000, + "built_form": 167 + } + }, + { + "pk": 803, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 8293.824, + "vacancy_rate": 0.000, + "square_feet_per_unit": 420.000, + "percent": 1.0000000, + "efficiency": 0.4760, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 3, + "gross_built_up_area": 17424.000, + "built_form": 168 + } + }, + { + "pk": 804, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 8701.110, + "vacancy_rate": 0.000, + "square_feet_per_unit": 352.500, + "percent": 1.0000000, + "efficiency": 0.3995, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 21780.000, + "built_form": 169 + } + }, + { + "pk": 805, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 10182.150, + "vacancy_rate": 0.000, + "square_feet_per_unit": 412.500, + "percent": 1.0000000, + "efficiency": 0.4675, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 21780.000, + "built_form": 170 + } + }, + { + "pk": 806, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 11848.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 480.000, + "percent": 1.0000000, + "efficiency": 0.5440, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 21780.000, + "built_form": 171 + } + }, + { + "pk": 807, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.0703864307, + "net_built_up_area": 7220.331, + "vacancy_rate": 0.000, + "square_feet_per_unit": 513.158, + "percent": 0.9500000, + "efficiency": 0.5816, + "household_size": 2.500, + "floor_area_ratio": 0.2850000000, + "building_use_definition": 3, + "gross_built_up_area": 12414.600, + "built_form": 172 + } + }, + { + "pk": 808, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8513000000, + "net_built_up_area": 555.390, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0150000000, + "building_use_definition": 5, + "gross_built_up_area": 653.400, + "built_form": 172 + } + }, + { + "pk": 809, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 173 + } + }, + { + "pk": 810, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.741, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000200000, + "building_use_definition": 2, + "gross_built_up_area": 0.871, + "built_form": 174 + } + }, + { + "pk": 811, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0086248800, + "net_built_up_area": 86.249, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0019800000, + "building_use_definition": 6, + "gross_built_up_area": 86.249, + "built_form": 174 + } + }, + { + "pk": 812, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 175 + } + }, + { + "pk": 813, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 2, + "gross_built_up_area": 0.436, + "built_form": 176 + } + }, + { + "pk": 814, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 6, + "gross_built_up_area": 43.124, + "built_form": 176 + } + }, + { + "pk": 815, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 2, + "gross_built_up_area": 0.436, + "built_form": 177 + } + }, + { + "pk": 816, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 6, + "gross_built_up_area": 43.124, + "built_form": 177 + } + }, + { + "pk": 817, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.741, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000200000, + "building_use_definition": 2, + "gross_built_up_area": 0.871, + "built_form": 178 + } + }, + { + "pk": 818, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042688800, + "net_built_up_area": 42.689, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9800000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009800000, + "building_use_definition": 6, + "gross_built_up_area": 42.689, + "built_form": 178 + } + }, + { + "pk": 819, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 1.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000300000, + "building_use_definition": 2, + "gross_built_up_area": 1.307, + "built_form": 179 + } + }, + { + "pk": 820, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042253200, + "net_built_up_area": 42.253, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9700000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009700000, + "building_use_definition": 6, + "gross_built_up_area": 42.253, + "built_form": 179 + } + }, + { + "pk": 821, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 180 + } + }, + { + "pk": 822, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 181 + } + }, + { + "pk": 823, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 1.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000300000, + "building_use_definition": 2, + "gross_built_up_area": 1.307, + "built_form": 182 + } + }, + { + "pk": 824, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0129373200, + "net_built_up_area": 129.373, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0029700000, + "building_use_definition": 6, + "gross_built_up_area": 129.373, + "built_form": 182 + } + }, + { + "pk": 825, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 183 + } + }, + { + "pk": 826, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 1.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000300000, + "building_use_definition": 2, + "gross_built_up_area": 1.307, + "built_form": 184 + } + }, + { + "pk": 827, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042253200, + "net_built_up_area": 42.253, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9700000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009700000, + "building_use_definition": 6, + "gross_built_up_area": 42.253, + "built_form": 184 + } + }, + { + "pk": 828, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 2, + "gross_built_up_area": 0.436, + "built_form": 185 + } + }, + { + "pk": 829, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 6, + "gross_built_up_area": 43.124, + "built_form": 185 + } + }, + { + "pk": 830, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 1.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000300000, + "building_use_definition": 2, + "gross_built_up_area": 1.307, + "built_form": 186 + } + }, + { + "pk": 831, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042253200, + "net_built_up_area": 42.253, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9700000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009700000, + "building_use_definition": 6, + "gross_built_up_area": 42.253, + "built_form": 186 + } + }, + { + "pk": 832, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.741, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000200000, + "building_use_definition": 2, + "gross_built_up_area": 0.871, + "built_form": 187 + } + }, + { + "pk": 833, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042688800, + "net_built_up_area": 42.689, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9800000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009800000, + "building_use_definition": 6, + "gross_built_up_area": 42.689, + "built_form": 187 + } + }, + { + "pk": 834, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 2, + "gross_built_up_area": 0.436, + "built_form": 188 + } + }, + { + "pk": 835, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 6, + "gross_built_up_area": 43.124, + "built_form": 188 + } + }, + { + "pk": 836, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 189 + } + }, + { + "pk": 837, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0566280000, + "net_built_up_area": 566.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0130000000, + "building_use_definition": 6, + "gross_built_up_area": 566.280, + "built_form": 190 + } + }, + { + "pk": 838, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0039204000, + "net_built_up_area": 39.204, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009000000, + "building_use_definition": 6, + "gross_built_up_area": 39.204, + "built_form": 191 + } + }, + { + "pk": 839, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0024684000, + "net_built_up_area": 1.851, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000500000, + "building_use_definition": 3, + "gross_built_up_area": 2.178, + "built_form": 191 + } + }, + { + "pk": 840, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0037026000, + "net_built_up_area": 1.851, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000500000, + "building_use_definition": 5, + "gross_built_up_area": 2.178, + "built_form": 191 + } + }, + { + "pk": 841, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6011315417, + "net_built_up_area": 0.370, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000100000, + "building_use_definition": 2, + "gross_built_up_area": 0.436, + "built_form": 192 + } + }, + { + "pk": 842, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043124400, + "net_built_up_area": 43.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9900000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009900000, + "building_use_definition": 6, + "gross_built_up_area": 43.124, + "built_form": 192 + } + }, + { + "pk": 843, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 193 + } + }, + { + "pk": 844, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 194 + } + }, + { + "pk": 845, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 195 + } + }, + { + "pk": 846, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0546242400, + "net_built_up_area": 40.968, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9500000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0009500000, + "building_use_definition": 3, + "gross_built_up_area": 41.382, + "built_form": 196 + } + }, + { + "pk": 847, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0037026000, + "net_built_up_area": 1.851, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000500000, + "building_use_definition": 5, + "gross_built_up_area": 2.178, + "built_form": 196 + } + }, + { + "pk": 848, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0041382000, + "net_built_up_area": 41.382, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9500000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009500000, + "building_use_definition": 6, + "gross_built_up_area": 41.382, + "built_form": 197 + } + }, + { + "pk": 849, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0037026000, + "net_built_up_area": 1.851, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000500000, + "building_use_definition": 5, + "gross_built_up_area": 2.178, + "built_form": 197 + } + }, + { + "pk": 850, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0043560000, + "net_built_up_area": 43.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 6, + "gross_built_up_area": 43.560, + "built_form": 198 + } + }, + { + "pk": 851, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0039204000, + "net_built_up_area": 39.204, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009000000, + "building_use_definition": 6, + "gross_built_up_area": 39.204, + "built_form": 199 + } + }, + { + "pk": 852, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0074052000, + "net_built_up_area": 3.703, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0001000000, + "building_use_definition": 5, + "gross_built_up_area": 4.356, + "built_form": 199 + } + }, + { + "pk": 853, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1698840000, + "net_built_up_area": 424.710, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.7500, + "household_size": 2.500, + "floor_area_ratio": 0.0130000000, + "building_use_definition": 4, + "gross_built_up_area": 566.280, + "built_form": 200 + } + }, + { + "pk": 854, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0740520000, + "net_built_up_area": 37.026, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0010000000, + "building_use_definition": 5, + "gross_built_up_area": 43.560, + "built_form": 201 + } + }, + { + "pk": 855, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5445000000, + "net_built_up_area": 1905.750, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0437500000, + "building_use_definition": 2, + "gross_built_up_area": 1905.750, + "built_form": 202 + } + }, + { + "pk": 856, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2000000000, + "net_built_up_area": 700.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0160698000, + "building_use_definition": 2, + "gross_built_up_area": 700.000, + "built_form": 203 + } + }, + { + "pk": 857, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6666666667, + "net_built_up_area": 2333.335, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0535660000, + "building_use_definition": 2, + "gross_built_up_area": 2333.335, + "built_form": 204 + } + }, + { + "pk": 858, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1000000000, + "net_built_up_area": 350.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0080349000, + "building_use_definition": 2, + "gross_built_up_area": 350.000, + "built_form": 205 + } + }, + { + "pk": 859, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1210000000, + "net_built_up_area": 423.499, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0097222000, + "building_use_definition": 2, + "gross_built_up_area": 423.499, + "built_form": 206 + } + }, + { + "pk": 860, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0500000000, + "net_built_up_area": 174.998, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0040174000, + "building_use_definition": 2, + "gross_built_up_area": 174.998, + "built_form": 207 + } + }, + { + "pk": 861, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0435600000, + "net_built_up_area": 152.460, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0035000000, + "building_use_definition": 2, + "gross_built_up_area": 152.460, + "built_form": 208 + } + }, + { + "pk": 862, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0478681319, + "net_built_up_area": 167.540, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0038462000, + "building_use_definition": 2, + "gross_built_up_area": 167.540, + "built_form": 209 + } + }, + { + "pk": 863, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2074285714, + "net_built_up_area": 726.001, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0166667000, + "building_use_definition": 2, + "gross_built_up_area": 726.001, + "built_form": 210 + } + }, + { + "pk": 864, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4356000000, + "net_built_up_area": 1524.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0350000000, + "building_use_definition": 2, + "gross_built_up_area": 1524.600, + "built_form": 211 + } + }, + { + "pk": 865, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5445000000, + "net_built_up_area": 1361.250, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0312500000, + "building_use_definition": 2, + "gross_built_up_area": 1361.250, + "built_form": 212 + } + }, + { + "pk": 866, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2722500000, + "net_built_up_area": 816.750, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0187500000, + "building_use_definition": 2, + "gross_built_up_area": 816.750, + "built_form": 213 + } + }, + { + "pk": 867, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.5124474845, + "net_built_up_area": 6046.764, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3998.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1388146000, + "building_use_definition": 2, + "gross_built_up_area": 6046.764, + "built_form": 214 + } + }, + { + "pk": 868, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5445000000, + "net_built_up_area": 1633.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0375000000, + "building_use_definition": 2, + "gross_built_up_area": 1633.500, + "built_form": 215 + } + }, + { + "pk": 869, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 305.3220923077, + "net_built_up_area": 595378.080, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1950.000, + "percent": 0.6000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 16.0800000000, + "building_use_definition": 2, + "gross_built_up_area": 700444.800, + "built_form": 216 + } + }, + { + "pk": 870, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 108.2505600000, + "net_built_up_area": 59537.808, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.6080000000, + "building_use_definition": 3, + "gross_built_up_area": 70044.480, + "built_form": 216 + } + }, + { + "pk": 871, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1349.5236480000, + "net_built_up_area": 337380.912, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.3400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 9.1120000000, + "building_use_definition": 5, + "gross_built_up_area": 396918.720, + "built_form": 216 + } + }, + { + "pk": 872, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 112.0933382084, + "net_built_up_area": 306575.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2735.000, + "percent": 0.4500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.2800000000, + "building_use_definition": 2, + "gross_built_up_area": 360676.800, + "built_form": 217 + } + }, + { + "pk": 873, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 71.7135157895, + "net_built_up_area": 34063.920, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9200000000, + "building_use_definition": 3, + "gross_built_up_area": 40075.200, + "built_form": 217 + } + }, + { + "pk": 874, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 973.2548571429, + "net_built_up_area": 340639.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 9.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 400752.000, + "built_form": 217 + } + }, + { + "pk": 875, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 330.5447431579, + "net_built_up_area": 628035.012, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1900.000, + "percent": 0.3300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 16.9620000000, + "building_use_definition": 2, + "gross_built_up_area": 738864.720, + "built_form": 218 + } + }, + { + "pk": 876, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5100.4055520000, + "net_built_up_area": 1275101.388, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.6700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 34.4380000000, + "building_use_definition": 5, + "gross_built_up_area": 1500119.280, + "built_form": 218 + } + }, + { + "pk": 877, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 148.4002080000, + "net_built_up_area": 74200.104, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0040000000, + "building_use_definition": 3, + "gross_built_up_area": 87294.240, + "built_form": 219 + } + }, + { + "pk": 878, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4649.8731840000, + "net_built_up_area": 1162468.296, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 31.3960000000, + "building_use_definition": 5, + "gross_built_up_area": 1367609.760, + "built_form": 219 + } + }, + { + "pk": 879, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.2475600000, + "net_built_up_area": 19623.780, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5300000000, + "building_use_definition": 3, + "gross_built_up_area": 23086.800, + "built_form": 220 + } + }, + { + "pk": 880, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3846.2608800000, + "net_built_up_area": 961565.220, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 25.9700000000, + "building_use_definition": 5, + "gross_built_up_area": 1131253.200, + "built_form": 220 + } + }, + { + "pk": 881, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.3192280000, + "net_built_up_area": 23659.614, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6390000000, + "building_use_definition": 3, + "gross_built_up_area": 27834.840, + "built_form": 221 + } + }, + { + "pk": 882, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3059.9767440000, + "net_built_up_area": 764994.186, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 20.6610000000, + "building_use_definition": 5, + "gross_built_up_area": 899993.160, + "built_form": 221 + } + }, + { + "pk": 883, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 44.4312000000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 3, + "gross_built_up_area": 26136.000, + "built_form": 222 + } + }, + { + "pk": 884, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2873.2176000000, + "net_built_up_area": 718304.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 19.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 845064.000, + "built_form": 222 + } + }, + { + "pk": 885, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.1705280000, + "net_built_up_area": 24585.264, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6640000000, + "building_use_definition": 3, + "gross_built_up_area": 28923.840, + "built_form": 223 + } + }, + { + "pk": 886, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4818.7117440000, + "net_built_up_area": 1204677.936, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 32.5360000000, + "building_use_definition": 5, + "gross_built_up_area": 1417268.160, + "built_form": 223 + } + }, + { + "pk": 887, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.9596960000, + "net_built_up_area": 5479.848, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1480000000, + "building_use_definition": 3, + "gross_built_up_area": 6446.880, + "built_form": 224 + } + }, + { + "pk": 888, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1074.0502080000, + "net_built_up_area": 268512.552, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 7.2520000000, + "building_use_definition": 5, + "gross_built_up_area": 315897.120, + "built_form": 224 + } + }, + { + "pk": 889, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.8068240000, + "net_built_up_area": 13403.412, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3620000000, + "building_use_definition": 3, + "gross_built_up_area": 15768.720, + "built_form": 225 + } + }, + { + "pk": 890, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2627.0687520000, + "net_built_up_area": 656767.188, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 17.7380000000, + "building_use_definition": 5, + "gross_built_up_area": 772667.280, + "built_form": 225 + } + }, + { + "pk": 891, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 166.4863200000, + "net_built_up_area": 83243.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.9800, + "household_size": 2.500, + "floor_area_ratio": 1.9500000000, + "building_use_definition": 3, + "gross_built_up_area": 84942.000, + "built_form": 226 + } + }, + { + "pk": 892, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2599.2252000000, + "net_built_up_area": 649806.300, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 17.5500000000, + "building_use_definition": 5, + "gross_built_up_area": 764478.000, + "built_form": 226 + } + }, + { + "pk": 893, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 264.5607770270, + "net_built_up_area": 313239.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 0.9400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.4600000000, + "building_use_definition": 2, + "gross_built_up_area": 368517.600, + "built_form": 227 + } + }, + { + "pk": 894, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 37.1636431227, + "net_built_up_area": 19994.040, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5400000000, + "building_use_definition": 3, + "gross_built_up_area": 23522.400, + "built_form": 227 + } + }, + { + "pk": 895, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 545.0088785047, + "net_built_up_area": 699791.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1284.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 18.9000000000, + "building_use_definition": 2, + "gross_built_up_area": 823284.000, + "built_form": 228 + } + }, + { + "pk": 896, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 672.4232307692, + "net_built_up_area": 576939.132, + "vacancy_rate": 0.000, + "square_feet_per_unit": 858.000, + "percent": 0.9800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 15.5820000000, + "building_use_definition": 2, + "gross_built_up_area": 678751.920, + "built_form": 229 + } + }, + { + "pk": 897, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.1650400000, + "net_built_up_area": 11774.268, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3180000000, + "building_use_definition": 3, + "gross_built_up_area": 13852.080, + "built_form": 229 + } + }, + { + "pk": 898, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 458.3936603774, + "net_built_up_area": 728845.920, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1590.000, + "percent": 1.0000000, + "efficiency": 0.9400, + "household_size": 2.500, + "floor_area_ratio": 17.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 775368.000, + "built_form": 230 + } + }, + { + "pk": 899, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 395.6855793991, + "net_built_up_area": 921947.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2330.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 24.9000000000, + "building_use_definition": 2, + "gross_built_up_area": 1084644.000, + "built_form": 231 + } + }, + { + "pk": 900, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 21780.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2000.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 2, + "gross_built_up_area": 21780.000, + "built_form": 232 + } + }, + { + "pk": 901, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 19602.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1800.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 2, + "gross_built_up_area": 19602.000, + "built_form": 233 + } + }, + { + "pk": 902, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.4160688666, + "net_built_up_area": 29592.050, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2841.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.6793400000, + "building_use_definition": 2, + "gross_built_up_area": 29592.050, + "built_form": 234 + } + }, + { + "pk": 903, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.2978723404, + "net_built_up_area": 26671.487, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2590.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.6122931000, + "building_use_definition": 2, + "gross_built_up_area": 26671.487, + "built_form": 235 + } + }, + { + "pk": 904, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 16335.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3750000000, + "building_use_definition": 2, + "gross_built_up_area": 16335.000, + "built_form": 236 + } + }, + { + "pk": 905, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.8900000000, + "net_built_up_area": 35937.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3300.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8250000000, + "building_use_definition": 2, + "gross_built_up_area": 35937.000, + "built_form": 237 + } + }, + { + "pk": 906, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 91.0014020270, + "net_built_up_area": 107745.660, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 0.9700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.9100000000, + "building_use_definition": 2, + "gross_built_up_area": 126759.600, + "built_form": 238 + } + }, + { + "pk": 907, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.1939405204, + "net_built_up_area": 3332.340, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0900000000, + "building_use_definition": 3, + "gross_built_up_area": 3920.400, + "built_form": 238 + } + }, + { + "pk": 908, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 124.1460000000, + "net_built_up_area": 94971.690, + "vacancy_rate": 0.000, + "square_feet_per_unit": 765.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.5650000000, + "building_use_definition": 2, + "gross_built_up_area": 111731.400, + "built_form": 239 + } + }, + { + "pk": 909, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.9940400000, + "net_built_up_area": 4998.510, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1350000000, + "building_use_definition": 3, + "gross_built_up_area": 5880.600, + "built_form": 239 + } + }, + { + "pk": 910, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 59.2416000000, + "net_built_up_area": 68127.840, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1150.000, + "percent": 0.9200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.8400000000, + "building_use_definition": 2, + "gross_built_up_area": 80150.400, + "built_form": 240 + } + }, + { + "pk": 911, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.8483200000, + "net_built_up_area": 5924.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1600000000, + "building_use_definition": 3, + "gross_built_up_area": 6969.600, + "built_form": 240 + } + }, + { + "pk": 912, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 103.5135483871, + "net_built_up_area": 96267.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 930.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.6000000000, + "building_use_definition": 2, + "gross_built_up_area": 113256.000, + "built_form": 241 + } + }, + { + "pk": 913, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 62.3209900990, + "net_built_up_area": 62944.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1010.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.7000000000, + "building_use_definition": 2, + "gross_built_up_area": 74052.000, + "built_form": 242 + } + }, + { + "pk": 914, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 78.7787234043, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 940.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 87120.000, + "built_form": 243 + } + }, + { + "pk": 915, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 111.0780000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 130680.000, + "built_form": 244 + } + }, + { + "pk": 916, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.6048571429, + "net_built_up_area": 66646.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1400.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 78408.000, + "built_form": 245 + } + }, + { + "pk": 917, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.8104000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 3, + "gross_built_up_area": 8712.000, + "built_form": 245 + } + }, + { + "pk": 918, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 199.9404000000, + "net_built_up_area": 149955.300, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.0500000000, + "building_use_definition": 2, + "gross_built_up_area": 176418.000, + "built_form": 246 + } + }, + { + "pk": 919, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 33.3234000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 3, + "gross_built_up_area": 19602.000, + "built_form": 246 + } + }, + { + "pk": 920, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.2594458438, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1191.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 247 + } + }, + { + "pk": 921, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.2320000000, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 248 + } + }, + { + "pk": 922, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.9985243483, + "net_built_up_area": 30492.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2033.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 2, + "gross_built_up_area": 30492.000, + "built_form": 249 + } + }, + { + "pk": 923, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.6015267176, + "net_built_up_area": 52272.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1965.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 2, + "gross_built_up_area": 52272.000, + "built_form": 250 + } + }, + { + "pk": 924, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.5035971223, + "net_built_up_area": 39204.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1668.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 2, + "gross_built_up_area": 39204.000, + "built_form": 251 + } + }, + { + "pk": 925, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.5688367129, + "net_built_up_area": 47916.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1874.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.1000000000, + "building_use_definition": 2, + "gross_built_up_area": 47916.000, + "built_form": 252 + } + }, + { + "pk": 926, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.1612903226, + "net_built_up_area": 65340.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3410.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 2, + "gross_built_up_area": 65340.000, + "built_form": 253 + } + }, + { + "pk": 927, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 30.4083769634, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1146.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 254 + } + }, + { + "pk": 928, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.2280000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 5, + "gross_built_up_area": 4356.000, + "built_form": 255 + } + }, + { + "pk": 929, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 8712.000, + "built_form": 256 + } + }, + { + "pk": 930, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.2280000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 5, + "gross_built_up_area": 4356.000, + "built_form": 257 + } + }, + { + "pk": 931, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 55.5390000000, + "net_built_up_area": 55539.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 2, + "gross_built_up_area": 65340.000, + "built_form": 258 + } + }, + { + "pk": 932, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 33.6600000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1100.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 43560.000, + "built_form": 259 + } + }, + { + "pk": 933, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.4100000000, + "net_built_up_area": 25918.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1020.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 2, + "gross_built_up_area": 30492.000, + "built_form": 260 + } + }, + { + "pk": 934, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 56.4803389831, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 590.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 2, + "gross_built_up_area": 39204.000, + "built_form": 261 + } + }, + { + "pk": 935, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 46.9343661972, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 710.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 2, + "gross_built_up_area": 39204.000, + "built_form": 262 + } + }, + { + "pk": 936, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.0623883022, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1231.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 263 + } + }, + { + "pk": 937, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 37.0260000000, + "net_built_up_area": 48133.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1300.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.3000000000, + "building_use_definition": 2, + "gross_built_up_area": 56628.000, + "built_form": 264 + } + }, + { + "pk": 938, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 53.7474193548, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 620.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 2, + "gross_built_up_area": 39204.000, + "built_form": 265 + } + }, + { + "pk": 939, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 8712.000, + "built_form": 266 + } + }, + { + "pk": 940, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 32.9120000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 267 + } + }, + { + "pk": 941, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 268 + } + }, + { + "pk": 942, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.5700000000, + "net_built_up_area": 9256.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2500000000, + "building_use_definition": 5, + "gross_built_up_area": 10890.000, + "built_form": 269 + } + }, + { + "pk": 943, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.6208000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 270 + } + }, + { + "pk": 944, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.3680000000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 5, + "gross_built_up_area": 26136.000, + "built_form": 271 + } + }, + { + "pk": 945, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 65.8240000000, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 5, + "gross_built_up_area": 34848.000, + "built_form": 272 + } + }, + { + "pk": 946, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 82.2800000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 43560.000, + "built_form": 273 + } + }, + { + "pk": 947, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 8712.000, + "built_form": 274 + } + }, + { + "pk": 948, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 41.1400000000, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 5, + "gross_built_up_area": 21780.000, + "built_form": 275 + } + }, + { + "pk": 949, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 276 + } + }, + { + "pk": 950, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9494400000, + "net_built_up_area": 2962.080, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0800000000, + "building_use_definition": 3, + "gross_built_up_area": 3484.800, + "built_form": 277 + } + }, + { + "pk": 951, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.3296000000, + "net_built_up_area": 11848.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.8000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3200000000, + "building_use_definition": 5, + "gross_built_up_area": 13939.200, + "built_form": 277 + } + }, + { + "pk": 952, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 3702.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1000000000, + "building_use_definition": 3, + "gross_built_up_area": 4356.000, + "built_form": 278 + } + }, + { + "pk": 953, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 74.0520000000, + "net_built_up_area": 33323.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.9000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 5, + "gross_built_up_area": 39204.000, + "built_form": 278 + } + }, + { + "pk": 954, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9620800000, + "net_built_up_area": 2221.560, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0600000000, + "building_use_definition": 3, + "gross_built_up_area": 2613.600, + "built_form": 279 + } + }, + { + "pk": 955, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.6966400000, + "net_built_up_area": 8886.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 0.8000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2400000000, + "building_use_definition": 5, + "gross_built_up_area": 10454.400, + "built_form": 279 + } + }, + { + "pk": 956, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 69.1152000000, + "net_built_up_area": 25918.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 5, + "gross_built_up_area": 30492.000, + "built_form": 280 + } + }, + { + "pk": 957, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.6840000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 281 + } + }, + { + "pk": 958, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 41.1400000000, + "net_built_up_area": 18513.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5000000000, + "building_use_definition": 5, + "gross_built_up_area": 21780.000, + "built_form": 282 + } + }, + { + "pk": 959, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 283 + } + }, + { + "pk": 960, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9241600000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 284 + } + }, + { + "pk": 961, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 285 + } + }, + { + "pk": 962, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 286 + } + }, + { + "pk": 963, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9241600000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 5, + "gross_built_up_area": 26136.000, + "built_form": 287 + } + }, + { + "pk": 964, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9494400000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 288 + } + }, + { + "pk": 965, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 289 + } + }, + { + "pk": 966, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 290 + } + }, + { + "pk": 967, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 291 + } + }, + { + "pk": 968, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 321.9652173913, + "net_built_up_area": 296208.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 920.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 8.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 348480.000, + "built_form": 292 + } + }, + { + "pk": 969, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 703.2578313253, + "net_built_up_area": 233481.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 332.000, + "percent": 1.0000000, + "efficiency": 0.8000, + "household_size": 2.500, + "floor_area_ratio": 6.7000000000, + "building_use_definition": 2, + "gross_built_up_area": 291852.000, + "built_form": 293 + } + }, + { + "pk": 970, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 240.0586813187, + "net_built_up_area": 218453.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 910.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 5.9000000000, + "building_use_definition": 2, + "gross_built_up_area": 257004.000, + "built_form": 294 + } + }, + { + "pk": 971, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 131.3422297297, + "net_built_up_area": 155509.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.2000000000, + "building_use_definition": 2, + "gross_built_up_area": 182952.000, + "built_form": 295 + } + }, + { + "pk": 972, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 111.0780000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 130680.000, + "built_form": 296 + } + }, + { + "pk": 973, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 93.8158783784, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1184.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 130680.000, + "built_form": 297 + } + }, + { + "pk": 974, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 52.8942857143, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1400.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 87120.000, + "built_form": 298 + } + }, + { + "pk": 975, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 130.6800000000, + "net_built_up_area": 99970.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 765.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.7000000000, + "building_use_definition": 2, + "gross_built_up_area": 117612.000, + "built_form": 299 + } + }, + { + "pk": 976, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 111.0780000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 130680.000, + "built_form": 300 + } + }, + { + "pk": 977, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 62.3209900990, + "net_built_up_area": 62944.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1010.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.7000000000, + "building_use_definition": 2, + "gross_built_up_area": 74052.000, + "built_form": 301 + } + }, + { + "pk": 978, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 78.7787234043, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 940.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 87120.000, + "built_form": 302 + } + }, + { + "pk": 979, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 222.1560000000, + "net_built_up_area": 166617.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.5000000000, + "building_use_definition": 2, + "gross_built_up_area": 196020.000, + "built_form": 303 + } + }, + { + "pk": 980, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 103.5135483871, + "net_built_up_area": 96267.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 930.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.6000000000, + "building_use_definition": 2, + "gross_built_up_area": 113256.000, + "built_form": 304 + } + }, + { + "pk": 981, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 64.3930434783, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1150.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 2, + "gross_built_up_area": 87120.000, + "built_form": 305 + } + }, + { + "pk": 982, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.4944000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 306 + } + }, + { + "pk": 983, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 32.9120000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 307 + } + }, + { + "pk": 984, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.2594458438, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1191.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 308 + } + }, + { + "pk": 985, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.5035971223, + "net_built_up_area": 39204.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1668.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.9000000000, + "building_use_definition": 2, + "gross_built_up_area": 39204.000, + "built_form": 309 + } + }, + { + "pk": 986, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.6015267176, + "net_built_up_area": 52272.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1965.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 2, + "gross_built_up_area": 52272.000, + "built_form": 310 + } + }, + { + "pk": 987, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.2320000000, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 311 + } + }, + { + "pk": 988, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.5688367129, + "net_built_up_area": 47916.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1874.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.1000000000, + "building_use_definition": 2, + "gross_built_up_area": 47916.000, + "built_form": 312 + } + }, + { + "pk": 989, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.9985243483, + "net_built_up_area": 30492.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2033.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7000000000, + "building_use_definition": 2, + "gross_built_up_area": 30492.000, + "built_form": 313 + } + }, + { + "pk": 990, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 30.4083769634, + "net_built_up_area": 34848.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1146.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 2, + "gross_built_up_area": 34848.000, + "built_form": 314 + } + }, + { + "pk": 991, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.1612903226, + "net_built_up_area": 65340.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3410.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.5000000000, + "building_use_definition": 2, + "gross_built_up_area": 65340.000, + "built_form": 315 + } + }, + { + "pk": 992, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.1566265060, + "net_built_up_area": 45921.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1575.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.0542169000, + "building_use_definition": 2, + "gross_built_up_area": 45921.688, + "built_form": 316 + } + }, + { + "pk": 993, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.2409638554, + "net_built_up_area": 48414.579, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1845.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.1114458000, + "building_use_definition": 2, + "gross_built_up_area": 48414.579, + "built_form": 317 + } + }, + { + "pk": 994, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 28.1032258065, + "net_built_up_area": 49883.226, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1775.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.1451613000, + "building_use_definition": 2, + "gross_built_up_area": 49883.226, + "built_form": 318 + } + }, + { + "pk": 995, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.0057405281, + "net_built_up_area": 34457.911, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1378.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7910448000, + "building_use_definition": 2, + "gross_built_up_area": 34457.911, + "built_form": 319 + } + }, + { + "pk": 996, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.3614986050, + "net_built_up_area": 26875.601, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1548.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.6169789000, + "building_use_definition": 2, + "gross_built_up_area": 26875.601, + "built_form": 320 + } + }, + { + "pk": 997, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.9210526316, + "net_built_up_area": 30886.841, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1940.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7090643000, + "building_use_definition": 2, + "gross_built_up_area": 30886.841, + "built_form": 321 + } + }, + { + "pk": 998, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.1566265060, + "net_built_up_area": 45921.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1575.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.0542169000, + "building_use_definition": 2, + "gross_built_up_area": 45921.688, + "built_form": 322 + } + }, + { + "pk": 999, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.4240000000, + "net_built_up_area": 24393.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1400.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5600000000, + "building_use_definition": 2, + "gross_built_up_area": 24393.600, + "built_form": 323 + } + }, + { + "pk": 1000, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.1566265060, + "net_built_up_area": 45921.688, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1575.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.0542169000, + "building_use_definition": 2, + "gross_built_up_area": 45921.688, + "built_form": 324 + } + }, + { + "pk": 1001, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 98.7360000000, + "net_built_up_area": 74052.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.0000000000, + "building_use_definition": 3, + "gross_built_up_area": 87120.000, + "built_form": 325 + } + }, + { + "pk": 1002, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 143.1672000000, + "net_built_up_area": 107375.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.9000000000, + "building_use_definition": 3, + "gross_built_up_area": 126324.000, + "built_form": 326 + } + }, + { + "pk": 1003, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 44.4312000000, + "net_built_up_area": 44431.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2000000000, + "building_use_definition": 4, + "gross_built_up_area": 52272.000, + "built_form": 327 + } + }, + { + "pk": 1004, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 77.7546000000, + "net_built_up_area": 77754.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.1000000000, + "building_use_definition": 4, + "gross_built_up_area": 91476.000, + "built_form": 328 + } + }, + { + "pk": 1005, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 103.6728000000, + "net_built_up_area": 103672.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.8000000000, + "building_use_definition": 4, + "gross_built_up_area": 121968.000, + "built_form": 329 + } + }, + { + "pk": 1006, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 99.9702000000, + "net_built_up_area": 99970.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.7000000000, + "building_use_definition": 4, + "gross_built_up_area": 117612.000, + "built_form": 330 + } + }, + { + "pk": 1007, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 85.1598000000, + "net_built_up_area": 85159.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.3000000000, + "building_use_definition": 4, + "gross_built_up_area": 100188.000, + "built_form": 331 + } + }, + { + "pk": 1008, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.6208000000, + "net_built_up_area": 29620.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8000000000, + "building_use_definition": 4, + "gross_built_up_area": 34848.000, + "built_form": 332 + } + }, + { + "pk": 1009, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 37.0260000000, + "net_built_up_area": 37026.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0000000000, + "building_use_definition": 4, + "gross_built_up_area": 43560.000, + "built_form": 333 + } + }, + { + "pk": 1010, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 85.1598000000, + "net_built_up_area": 42579.900, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.4250, + "household_size": 2.500, + "floor_area_ratio": 2.3000000000, + "building_use_definition": 4, + "gross_built_up_area": 100188.000, + "built_form": 334 + } + }, + { + "pk": 1011, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.0294000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 4, + "gross_built_up_area": 19602.000, + "built_form": 335 + } + }, + { + "pk": 1012, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.2899500000, + "net_built_up_area": 25547.940, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6900000000, + "building_use_definition": 4, + "gross_built_up_area": 30056.400, + "built_form": 336 + } + }, + { + "pk": 1013, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.2676500000, + "net_built_up_area": 15921.180, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4300000000, + "building_use_definition": 4, + "gross_built_up_area": 18730.800, + "built_form": 337 + } + }, + { + "pk": 1014, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 20.9814000000, + "net_built_up_area": 25177.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6800000000, + "building_use_definition": 4, + "gross_built_up_area": 29620.800, + "built_form": 338 + } + }, + { + "pk": 1015, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1619887500, + "net_built_up_area": 32.398, + "vacancy_rate": 0.000, + "square_feet_per_unit": 200.000, + "percent": 0.0125000, + "efficiency": 0.3400, + "household_size": 2.500, + "floor_area_ratio": 0.0021875000, + "building_use_definition": 5, + "gross_built_up_area": 95.287, + "built_form": 339 + } + }, + { + "pk": 1016, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.8624982560, + "net_built_up_area": 3646.874, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.4850000, + "efficiency": 0.9864, + "household_size": 2.500, + "floor_area_ratio": 0.0848750000, + "building_use_definition": 7, + "gross_built_up_area": 3697.155, + "built_form": 339 + } + }, + { + "pk": 1017, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7241850000, + "net_built_up_area": 543.139, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0750000, + "efficiency": 0.9500, + "household_size": 2.500, + "floor_area_ratio": 0.0131250000, + "building_use_definition": 10, + "gross_built_up_area": 571.725, + "built_form": 339 + } + }, + { + "pk": 1018, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1132015500, + "net_built_up_area": 56.601, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0075000, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0013125000, + "building_use_definition": 8, + "gross_built_up_area": 57.172, + "built_form": 339 + } + }, + { + "pk": 1019, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9097299780, + "net_built_up_area": 2932.297, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.4275000, + "efficiency": 0.8998, + "household_size": 2.500, + "floor_area_ratio": 0.0748125000, + "building_use_definition": 9, + "gross_built_up_area": 3258.832, + "built_form": 339 + } + }, + { + "pk": 1020, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.0304542840, + "net_built_up_area": 2932.044, + "vacancy_rate": 0.000, + "square_feet_per_unit": 324.684, + "percent": 0.9875000, + "efficiency": 0.3895, + "household_size": 2.500, + "floor_area_ratio": 0.1728125000, + "building_use_definition": 3, + "gross_built_up_area": 7527.713, + "built_form": 339 + } + }, + { + "pk": 1021, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0647955000, + "net_built_up_area": 32.398, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0050000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0008750000, + "building_use_definition": 15, + "gross_built_up_area": 38.115, + "built_form": 339 + } + }, + { + "pk": 1022, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 60.6787765396, + "net_built_up_area": 31783.118, + "vacancy_rate": 0.000, + "square_feet_per_unit": 523.793, + "percent": 0.4640000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8584000000, + "building_use_definition": 9, + "gross_built_up_area": 37391.904, + "built_form": 340 + } + }, + { + "pk": 1023, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 51.3592499853, + "net_built_up_area": 36714.982, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.5360000, + "efficiency": 0.8500, + "household_size": 1.345, + "floor_area_ratio": 0.9916000000, + "building_use_definition": 2, + "gross_built_up_area": 43194.096, + "built_form": 340 + } + }, + { + "pk": 1024, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 60.6787765396, + "net_built_up_area": 31783.118, + "vacancy_rate": 0.000, + "square_feet_per_unit": 523.793, + "percent": 0.4640000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8584000000, + "building_use_definition": 3, + "gross_built_up_area": 37391.904, + "built_form": 340 + } + }, + { + "pk": 1025, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 51.3592499853, + "net_built_up_area": 36714.982, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.5360000, + "efficiency": 0.8500, + "household_size": 1.345, + "floor_area_ratio": 0.9916000000, + "building_use_definition": 21, + "gross_built_up_area": 43194.096, + "built_form": 340 + } + }, + { + "pk": 1026, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 152.2435068000, + "net_built_up_area": 38060.877, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.0770000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0279500000, + "building_use_definition": 5, + "gross_built_up_area": 44777.502, + "built_form": 341 + } + }, + { + "pk": 1027, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 42.5385649764, + "net_built_up_area": 20513.330, + "vacancy_rate": 0.000, + "square_feet_per_unit": 482.229, + "percent": 0.0415000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5540250000, + "building_use_definition": 7, + "gross_built_up_area": 24133.329, + "built_form": 341 + } + }, + { + "pk": 1028, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 332.3458653682, + "net_built_up_area": 421149.678, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1267.203, + "percent": 0.8595000, + "efficiency": 0.8426, + "household_size": 2.282, + "floor_area_ratio": 11.4743250000, + "building_use_definition": 2, + "gross_built_up_area": 499821.597, + "built_form": 341 + } + }, + { + "pk": 1029, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.0769105044, + "net_built_up_area": 10961.532, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0220000, + "efficiency": 0.8568, + "household_size": 2.500, + "floor_area_ratio": 0.2937000000, + "building_use_definition": 13, + "gross_built_up_area": 12793.572, + "built_form": 341 + } + }, + { + "pk": 1030, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 332.3458653682, + "net_built_up_area": 421149.678, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1267.203, + "percent": 0.8595000, + "efficiency": 0.8426, + "household_size": 2.282, + "floor_area_ratio": 11.4743250000, + "building_use_definition": 14, + "gross_built_up_area": 499821.597, + "built_form": 341 + } + }, + { + "pk": 1031, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 66.5984323516, + "net_built_up_area": 10959.904, + "vacancy_rate": 0.000, + "square_feet_per_unit": 164.567, + "percent": 0.0635000, + "efficiency": 0.2968, + "household_size": 2.500, + "floor_area_ratio": 0.8477250000, + "building_use_definition": 3, + "gross_built_up_area": 36926.901, + "built_form": 341 + } + }, + { + "pk": 1032, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 152.2435068000, + "net_built_up_area": 38060.877, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.0770000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0279500000, + "building_use_definition": 15, + "gross_built_up_area": 44777.502, + "built_form": 341 + } + }, + { + "pk": 1033, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.9747200000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 8, + "gross_built_up_area": 8712.000, + "built_form": 342 + } + }, + { + "pk": 1034, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.9747200000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 8712.000, + "built_form": 342 + } + }, + { + "pk": 1035, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.0889230769, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 650.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 8, + "gross_built_up_area": 13068.000, + "built_form": 343 + } + }, + { + "pk": 1036, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.0889230769, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 650.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 343 + } + }, + { + "pk": 1037, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.8746664857, + "net_built_up_area": 9320.185, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1355.729, + "percent": 0.8990000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2517200000, + "building_use_definition": 4, + "gross_built_up_area": 10964.923, + "built_form": 344 + } + }, + { + "pk": 1038, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.5918200000, + "net_built_up_area": 1036.728, + "vacancy_rate": 0.000, + "square_feet_per_unit": 400.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0280000000, + "building_use_definition": 5, + "gross_built_up_area": 1219.680, + "built_form": 344 + } + }, + { + "pk": 1039, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0138230400, + "net_built_up_area": 10.367, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0010000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0002800000, + "building_use_definition": 7, + "gross_built_up_area": 12.197, + "built_form": 344 + } + }, + { + "pk": 1040, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.8746664857, + "net_built_up_area": 9320.185, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1355.729, + "percent": 0.8990000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2517200000, + "building_use_definition": 18, + "gross_built_up_area": 10964.923, + "built_form": 344 + } + }, + { + "pk": 1041, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0138230400, + "net_built_up_area": 10.367, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0010000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0002800000, + "building_use_definition": 3, + "gross_built_up_area": 12.197, + "built_form": 344 + } + }, + { + "pk": 1042, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.5918200000, + "net_built_up_area": 1036.728, + "vacancy_rate": 0.000, + "square_feet_per_unit": 400.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0280000000, + "building_use_definition": 15, + "gross_built_up_area": 1219.680, + "built_form": 344 + } + }, + { + "pk": 1043, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8513000000, + "net_built_up_area": 9256.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2500000000, + "building_use_definition": 8, + "gross_built_up_area": 10890.000, + "built_form": 345 + } + }, + { + "pk": 1044, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8513000000, + "net_built_up_area": 9256.500, + "vacancy_rate": 0.000, + "square_feet_per_unit": 5000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2500000000, + "building_use_definition": 5, + "gross_built_up_area": 10890.000, + "built_form": 345 + } + }, + { + "pk": 1045, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 309.0728704663, + "net_built_up_area": 238604.256, + "vacancy_rate": 0.000, + "square_feet_per_unit": 772.000, + "percent": 1.0000000, + "efficiency": 0.8350, + "household_size": 2.500, + "floor_area_ratio": 6.5600000000, + "building_use_definition": 2, + "gross_built_up_area": 285753.600, + "built_form": 346 + } + }, + { + "pk": 1046, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 309.0728704663, + "net_built_up_area": 238604.256, + "vacancy_rate": 0.000, + "square_feet_per_unit": 772.000, + "percent": 1.0000000, + "efficiency": 0.8350, + "household_size": 2.500, + "floor_area_ratio": 6.5600000000, + "building_use_definition": 14, + "gross_built_up_area": 285753.600, + "built_form": 346 + } + }, + { + "pk": 1047, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 8, + "gross_built_up_area": 17424.000, + "built_form": 347 + } + }, + { + "pk": 1048, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9368000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3000.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 347 + } + }, + { + "pk": 1049, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1827.1044188813, + "net_built_up_area": 456776.105, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9560000, + "efficiency": 0.8754, + "household_size": 2.500, + "floor_area_ratio": 11.9786800000, + "building_use_definition": 5, + "gross_built_up_area": 521791.301, + "built_form": 348 + } + }, + { + "pk": 1050, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.5574312000, + "net_built_up_area": 9278.716, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2506000000, + "building_use_definition": 7, + "gross_built_up_area": 10916.136, + "built_form": 348 + } + }, + { + "pk": 1051, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.3508440400, + "net_built_up_area": 4175.422, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0090000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1127700000, + "building_use_definition": 13, + "gross_built_up_area": 4912.261, + "built_form": 348 + } + }, + { + "pk": 1052, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.9180734000, + "net_built_up_area": 6959.037, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0150000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1879500000, + "building_use_definition": 9, + "gross_built_up_area": 8187.102, + "built_form": 348 + } + }, + { + "pk": 1053, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.8300822397, + "net_built_up_area": 6959.692, + "vacancy_rate": 0.000, + "square_feet_per_unit": 170.455, + "percent": 0.0440000, + "efficiency": 0.2898, + "household_size": 2.500, + "floor_area_ratio": 0.5513200000, + "building_use_definition": 3, + "gross_built_up_area": 24015.499, + "built_form": 348 + } + }, + { + "pk": 1054, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1827.1044188813, + "net_built_up_area": 456776.105, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9560000, + "efficiency": 0.8754, + "household_size": 2.500, + "floor_area_ratio": 11.9786800000, + "building_use_definition": 15, + "gross_built_up_area": 521791.301, + "built_form": 348 + } + }, + { + "pk": 1055, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4389199908, + "net_built_up_area": 13962.773, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4214.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3205411600, + "building_use_definition": 2, + "gross_built_up_area": 13962.773, + "built_form": 349 + } + }, + { + "pk": 1056, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4389199908, + "net_built_up_area": 13962.773, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4214.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.3205411600, + "building_use_definition": 11, + "gross_built_up_area": 13962.773, + "built_form": 349 + } + }, + { + "pk": 1057, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.0355140187, + "net_built_up_area": 18786.239, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2322.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4312727000, + "building_use_definition": 2, + "gross_built_up_area": 18786.239, + "built_form": 350 + } + }, + { + "pk": 1058, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.0355140187, + "net_built_up_area": 18786.239, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2322.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.4312727000, + "building_use_definition": 11, + "gross_built_up_area": 18786.239, + "built_form": 350 + } + }, + { + "pk": 1059, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1211944136, + "net_built_up_area": 25373.261, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2409.300, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5824899300, + "building_use_definition": 2, + "gross_built_up_area": 25373.261, + "built_form": 351 + } + }, + { + "pk": 1060, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1211944136, + "net_built_up_area": 25373.261, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2409.300, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5824899300, + "building_use_definition": 26, + "gross_built_up_area": 25373.261, + "built_form": 351 + } + }, + { + "pk": 1061, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 531.7971563008, + "net_built_up_area": 655906.913, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1233.378, + "percent": 0.9910000, + "efficiency": 0.8909, + "household_size": 2.478, + "floor_area_ratio": 16.9015050000, + "building_use_definition": 2, + "gross_built_up_area": 736229.558, + "built_form": 352 + } + }, + { + "pk": 1062, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.6295686000, + "net_built_up_area": 5683.306, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.0090000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1534950000, + "building_use_definition": 3, + "gross_built_up_area": 6686.242, + "built_form": 352 + } + }, + { + "pk": 1063, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 531.7971563008, + "net_built_up_area": 655906.913, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1233.378, + "percent": 0.9910000, + "efficiency": 0.8909, + "household_size": 2.478, + "floor_area_ratio": 16.9015050000, + "building_use_definition": 14, + "gross_built_up_area": 736229.558, + "built_form": 352 + } + }, + { + "pk": 1064, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.6295686000, + "net_built_up_area": 5683.306, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.0090000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1534950000, + "building_use_definition": 7, + "gross_built_up_area": 6686.242, + "built_form": 352 + } + }, + { + "pk": 1065, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9241600000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 8, + "gross_built_up_area": 26136.000, + "built_form": 353 + } + }, + { + "pk": 1066, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9241600000, + "net_built_up_area": 22215.600, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6000000000, + "building_use_definition": 5, + "gross_built_up_area": 26136.000, + "built_form": 353 + } + }, + { + "pk": 1067, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 103.4182462500, + "net_built_up_area": 31025.474, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.1025000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8379375000, + "building_use_definition": 5, + "gross_built_up_area": 36500.558, + "built_form": 354 + } + }, + { + "pk": 1068, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 36.7822845570, + "net_built_up_area": 18161.253, + "vacancy_rate": 0.000, + "square_feet_per_unit": 493.750, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4905000000, + "building_use_definition": 7, + "gross_built_up_area": 21366.180, + "built_form": 354 + } + }, + { + "pk": 1069, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 193.6890229173, + "net_built_up_area": 248960.510, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1285.362, + "percent": 0.8225000, + "efficiency": 0.8500, + "household_size": 2.126, + "floor_area_ratio": 6.7239375000, + "building_use_definition": 2, + "gross_built_up_area": 292894.718, + "built_form": 354 + } + }, + { + "pk": 1070, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 193.6890229173, + "net_built_up_area": 248960.510, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1285.362, + "percent": 0.8225000, + "efficiency": 0.8500, + "household_size": 2.126, + "floor_area_ratio": 6.7239375000, + "building_use_definition": 14, + "gross_built_up_area": 292894.718, + "built_form": 354 + } + }, + { + "pk": 1071, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.2551150000, + "net_built_up_area": 4540.313, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0150000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1226250000, + "building_use_definition": 9, + "gross_built_up_area": 5341.545, + "built_form": 354 + } + }, + { + "pk": 1072, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 41.2755750000, + "net_built_up_area": 4540.313, + "vacancy_rate": 0.000, + "square_feet_per_unit": 110.000, + "percent": 0.0750000, + "efficiency": 0.1700, + "household_size": 2.500, + "floor_area_ratio": 0.6131250000, + "building_use_definition": 3, + "gross_built_up_area": 26707.725, + "built_form": 354 + } + }, + { + "pk": 1073, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 103.4182462500, + "net_built_up_area": 31025.474, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.1025000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8379375000, + "building_use_definition": 15, + "gross_built_up_area": 36500.558, + "built_form": 354 + } + }, + { + "pk": 1074, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 639.5400000000, + "net_built_up_area": 271548.684, + "vacancy_rate": 0.000, + "square_feet_per_unit": 424.600, + "percent": 1.0000000, + "efficiency": 0.6562, + "household_size": 2.500, + "floor_area_ratio": 9.5000000000, + "building_use_definition": 3, + "gross_built_up_area": 413820.000, + "built_form": 355 + } + }, + { + "pk": 1075, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 493.7248800000, + "net_built_up_area": 271548.684, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.7720000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 7.3340000000, + "building_use_definition": 17, + "gross_built_up_area": 319469.040, + "built_form": 355 + } + }, + { + "pk": 1076, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 145.8151200000, + "net_built_up_area": 80198.316, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.2280000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.1660000000, + "building_use_definition": 7, + "gross_built_up_area": 94350.960, + "built_form": 355 + } + }, + { + "pk": 1077, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.8637264000, + "net_built_up_area": 9863.726, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.1800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2664000000, + "building_use_definition": 9, + "gross_built_up_area": 11604.384, + "built_form": 356 + } + }, + { + "pk": 1078, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.8310927835, + "net_built_up_area": 27399.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1455.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 1.250, + "floor_area_ratio": 0.7400000000, + "building_use_definition": 2, + "gross_built_up_area": 32234.400, + "built_form": 356 + } + }, + { + "pk": 1079, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.5355136000, + "net_built_up_area": 17535.514, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.3200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4736000000, + "building_use_definition": 10, + "gross_built_up_area": 20630.016, + "built_form": 356 + } + }, + { + "pk": 1080, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 27.3992400000, + "net_built_up_area": 9863.726, + "vacancy_rate": 0.000, + "square_feet_per_unit": 360.000, + "percent": 0.5000000, + "efficiency": 0.3060, + "household_size": 2.500, + "floor_area_ratio": 0.7400000000, + "building_use_definition": 3, + "gross_built_up_area": 32234.400, + "built_form": 356 + } + }, + { + "pk": 1081, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.8310927835, + "net_built_up_area": 27399.240, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1455.000, + "percent": 0.5000000, + "efficiency": 0.8500, + "household_size": 1.250, + "floor_area_ratio": 0.7400000000, + "building_use_definition": 14, + "gross_built_up_area": 32234.400, + "built_form": 356 + } + }, + { + "pk": 1082, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 36.4741220978, + "net_built_up_area": 18585.389, + "vacancy_rate": 0.000, + "square_feet_per_unit": 509.550, + "percent": 0.1233000, + "efficiency": 0.8403, + "household_size": 2.500, + "floor_area_ratio": 0.5077494000, + "building_use_definition": 9, + "gross_built_up_area": 22117.564, + "built_form": 357 + } + }, + { + "pk": 1083, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 168.2658184029, + "net_built_up_area": 131200.729, + "vacancy_rate": 0.000, + "square_feet_per_unit": 779.723, + "percent": 0.8699000, + "efficiency": 0.8408, + "household_size": 2.206, + "floor_area_ratio": 3.5822482000, + "building_use_definition": 2, + "gross_built_up_area": 156042.732, + "built_form": 357 + } + }, + { + "pk": 1084, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 38.4866639032, + "net_built_up_area": 18585.864, + "vacancy_rate": 0.000, + "square_feet_per_unit": 482.917, + "percent": 0.1301000, + "efficiency": 0.7964, + "household_size": 2.500, + "floor_area_ratio": 0.5357518000, + "building_use_definition": 3, + "gross_built_up_area": 23337.348, + "built_form": 357 + } + }, + { + "pk": 1085, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 168.2658184029, + "net_built_up_area": 131200.729, + "vacancy_rate": 0.000, + "square_feet_per_unit": 779.723, + "percent": 0.8699000, + "efficiency": 0.8408, + "household_size": 2.206, + "floor_area_ratio": 3.5822482000, + "building_use_definition": 14, + "gross_built_up_area": 156042.732, + "built_form": 357 + } + }, + { + "pk": 1086, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1236020818, + "net_built_up_area": 1036.817, + "vacancy_rate": 0.000, + "square_feet_per_unit": 488.235, + "percent": 0.0068000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0280024000, + "building_use_definition": 7, + "gross_built_up_area": 1219.785, + "built_form": 357 + } + }, + { + "pk": 1087, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 268.5811990687, + "net_built_up_area": 83748.184, + "vacancy_rate": 0.000, + "square_feet_per_unit": 311.817, + "percent": 0.8750000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.2618750000, + "building_use_definition": 5, + "gross_built_up_area": 98527.275, + "built_form": 358 + } + }, + { + "pk": 1088, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.8284884000, + "net_built_up_area": 1914.244, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0517000000, + "building_use_definition": 7, + "gross_built_up_area": 2252.052, + "built_form": 358 + } + }, + { + "pk": 1089, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.6569768000, + "net_built_up_area": 7656.977, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0800000, + "efficiency": 0.8500, + "household_size": 1.000, + "floor_area_ratio": 0.2068000000, + "building_use_definition": 2, + "gross_built_up_area": 9008.208, + "built_form": 358 + } + }, + { + "pk": 1090, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9571221000, + "net_built_up_area": 478.561, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0050000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0129250000, + "building_use_definition": 13, + "gross_built_up_area": 563.013, + "built_form": 358 + } + }, + { + "pk": 1091, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.6569768000, + "net_built_up_area": 7656.977, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0800000, + "efficiency": 0.8500, + "household_size": 1.000, + "floor_area_ratio": 0.2068000000, + "building_use_definition": 14, + "gross_built_up_area": 9008.208, + "built_form": 358 + } + }, + { + "pk": 1092, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.8284884000, + "net_built_up_area": 1914.244, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0517000000, + "building_use_definition": 9, + "gross_built_up_area": 2252.052, + "built_form": 358 + } + }, + { + "pk": 1093, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.6146142263, + "net_built_up_area": 1914.357, + "vacancy_rate": 0.000, + "square_feet_per_unit": 222.222, + "percent": 0.0450000, + "efficiency": 0.3778, + "household_size": 2.500, + "floor_area_ratio": 0.1163250000, + "building_use_definition": 3, + "gross_built_up_area": 5067.117, + "built_form": 358 + } + }, + { + "pk": 1094, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 268.5811990687, + "net_built_up_area": 83748.184, + "vacancy_rate": 0.000, + "square_feet_per_unit": 311.817, + "percent": 0.8750000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 2.2618750000, + "building_use_definition": 15, + "gross_built_up_area": 98527.275, + "built_form": 358 + } + }, + { + "pk": 1095, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 64.6646457600, + "net_built_up_area": 32332.323, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0400000, + "efficiency": 0.9150, + "household_size": 2.500, + "floor_area_ratio": 0.8112000000, + "building_use_definition": 7, + "gross_built_up_area": 35335.872, + "built_form": 359 + } + }, + { + "pk": 1096, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 64.6646457600, + "net_built_up_area": 32332.323, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0400000, + "efficiency": 0.9150, + "household_size": 2.500, + "floor_area_ratio": 0.8112000000, + "building_use_definition": 3, + "gross_built_up_area": 35335.872, + "built_form": 359 + } + }, + { + "pk": 1097, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2883.4071552000, + "net_built_up_area": 720851.789, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 19.4688000000, + "building_use_definition": 5, + "gross_built_up_area": 848060.928, + "built_form": 359 + } + }, + { + "pk": 1098, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2883.4071552000, + "net_built_up_area": 720851.789, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.9600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 19.4688000000, + "building_use_definition": 15, + "gross_built_up_area": 848060.928, + "built_form": 359 + } + }, + { + "pk": 1099, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.3536616000, + "net_built_up_area": 3176.831, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0220000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0858000000, + "building_use_definition": 9, + "gross_built_up_area": 3737.448, + "built_form": 360 + } + }, + { + "pk": 1100, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.5521120000, + "net_built_up_area": 3176.831, + "vacancy_rate": 0.000, + "square_feet_per_unit": 275.000, + "percent": 0.0400000, + "efficiency": 0.4675, + "household_size": 2.500, + "floor_area_ratio": 0.1560000000, + "building_use_definition": 3, + "gross_built_up_area": 6795.360, + "built_form": 360 + } + }, + { + "pk": 1101, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.1984504000, + "net_built_up_area": 2599.225, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0180000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0702000000, + "building_use_definition": 7, + "gross_built_up_area": 3057.912, + "built_form": 360 + } + }, + { + "pk": 1102, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 531.2577853743, + "net_built_up_area": 138625.344, + "vacancy_rate": 0.000, + "square_feet_per_unit": 260.938, + "percent": 0.9600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.7440000000, + "building_use_definition": 5, + "gross_built_up_area": 163088.640, + "built_form": 360 + } + }, + { + "pk": 1103, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 531.2577853743, + "net_built_up_area": 138625.344, + "vacancy_rate": 0.000, + "square_feet_per_unit": 260.938, + "percent": 0.9600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.7440000000, + "building_use_definition": 15, + "gross_built_up_area": 163088.640, + "built_form": 360 + } + }, + { + "pk": 1104, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.5137312840, + "net_built_up_area": 2878.433, + "vacancy_rate": 0.000, + "square_feet_per_unit": 250.000, + "percent": 0.1500000, + "efficiency": 0.2833, + "household_size": 2.500, + "floor_area_ratio": 0.2332500000, + "building_use_definition": 3, + "gross_built_up_area": 10160.370, + "built_form": 361 + } + }, + { + "pk": 1105, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 47.4997297500, + "net_built_up_area": 47499.730, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.8250000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.2828750000, + "building_use_definition": 20, + "gross_built_up_area": 55882.035, + "built_form": 361 + } + }, + { + "pk": 1106, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.6767240000, + "net_built_up_area": 5757.543, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1555000000, + "building_use_definition": 13, + "gross_built_up_area": 6773.580, + "built_form": 361 + } + }, + { + "pk": 1107, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 48.9387239902, + "net_built_up_area": 1439.386, + "vacancy_rate": 0.000, + "square_feet_per_unit": 29.412, + "percent": 0.8500000, + "efficiency": 0.0250, + "household_size": 2.500, + "floor_area_ratio": 1.3217500000, + "building_use_definition": 4, + "gross_built_up_area": 57575.430, + "built_form": 361 + } + }, + { + "pk": 1108, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.8383620000, + "net_built_up_area": 2878.772, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0777500000, + "building_use_definition": 9, + "gross_built_up_area": 3386.790, + "built_form": 361 + } + }, + { + "pk": 1109, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4393857500, + "net_built_up_area": 1439.386, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0250000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0388750000, + "building_use_definition": 18, + "gross_built_up_area": 1693.395, + "built_form": 361 + } + }, + { + "pk": 1110, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6871936000, + "net_built_up_area": 2132.698, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 0.1600000, + "efficiency": 0.6375, + "household_size": 2.500, + "floor_area_ratio": 0.0768000000, + "building_use_definition": 5, + "gross_built_up_area": 3345.408, + "built_form": 362 + } + }, + { + "pk": 1111, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.7961477632, + "net_built_up_area": 2097.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1100000, + "efficiency": 0.9118, + "household_size": 2.500, + "floor_area_ratio": 0.0528000000, + "building_use_definition": 7, + "gross_built_up_area": 2299.968, + "built_form": 362 + } + }, + { + "pk": 1112, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9813196800, + "net_built_up_area": 735.990, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0400000, + "efficiency": 0.8800, + "household_size": 2.500, + "floor_area_ratio": 0.0192000000, + "building_use_definition": 10, + "gross_built_up_area": 836.352, + "built_form": 362 + } + }, + { + "pk": 1113, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4217984000, + "net_built_up_area": 710.899, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0192000000, + "building_use_definition": 16, + "gross_built_up_area": 836.352, + "built_form": 362 + } + }, + { + "pk": 1114, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 16.3929536990, + "net_built_up_area": 13007.448, + "vacancy_rate": 0.000, + "square_feet_per_unit": 793.478, + "percent": 0.6900000, + "efficiency": 0.9016, + "household_size": 2.500, + "floor_area_ratio": 0.3312000000, + "building_use_definition": 9, + "gross_built_up_area": 14427.072, + "built_form": 362 + } + }, + { + "pk": 1115, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.9566545950, + "net_built_up_area": 13007.448, + "vacancy_rate": 0.000, + "square_feet_per_unit": 651.785, + "percent": 0.8400000, + "efficiency": 0.7406, + "household_size": 2.500, + "floor_area_ratio": 0.4032000000, + "building_use_definition": 3, + "gross_built_up_area": 17563.392, + "built_form": 362 + } + }, + { + "pk": 1116, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.2653952000, + "net_built_up_area": 2132.698, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.1200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0576000000, + "building_use_definition": 15, + "gross_built_up_area": 2509.056, + "built_form": 362 + } + }, + { + "pk": 1117, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.3210787200, + "net_built_up_area": 3160.539, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0440000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0853600000, + "building_use_definition": 9, + "gross_built_up_area": 3718.282, + "built_form": 363 + } + }, + { + "pk": 1118, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 64.4306837994, + "net_built_up_area": 68669.901, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1065.795, + "percent": 0.9560000, + "efficiency": 0.8500, + "household_size": 2.394, + "floor_area_ratio": 1.8546400000, + "building_use_definition": 2, + "gross_built_up_area": 80788.118, + "built_form": 363 + } + }, + { + "pk": 1119, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.3210787200, + "net_built_up_area": 3160.539, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0440000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0853600000, + "building_use_definition": 3, + "gross_built_up_area": 3718.282, + "built_form": 363 + } + }, + { + "pk": 1120, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 64.4306837994, + "net_built_up_area": 68669.901, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1065.795, + "percent": 0.9560000, + "efficiency": 0.8500, + "household_size": 2.394, + "floor_area_ratio": 1.8546400000, + "building_use_definition": 14, + "gross_built_up_area": 80788.118, + "built_form": 363 + } + }, + { + "pk": 1121, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.3920000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 412.500, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 25, + "gross_built_up_area": 19602.000, + "built_form": 364 + } + }, + { + "pk": 1122, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.3920000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 412.500, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 5, + "gross_built_up_area": 19602.000, + "built_form": 364 + } + }, + { + "pk": 1123, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.2593684211, + "net_built_up_area": 4813.380, + "vacancy_rate": 0.000, + "square_feet_per_unit": 427.500, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1300000000, + "building_use_definition": 25, + "gross_built_up_area": 5662.800, + "built_form": 365 + } + }, + { + "pk": 1124, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.2593684211, + "net_built_up_area": 4813.380, + "vacancy_rate": 0.000, + "square_feet_per_unit": 427.500, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1300000000, + "building_use_definition": 5, + "gross_built_up_area": 5662.800, + "built_form": 365 + } + }, + { + "pk": 1125, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0085484990, + "net_built_up_area": 248.822, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0057121600, + "building_use_definition": 2, + "gross_built_up_area": 248.822, + "built_form": 366 + } + }, + { + "pk": 1126, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0085484990, + "net_built_up_area": 248.822, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3500.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0057121600, + "building_use_definition": 11, + "gross_built_up_area": 248.822, + "built_form": 366 + } + }, + { + "pk": 1127, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.5719200000, + "net_built_up_area": 1785.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 4.1000000000, + "building_use_definition": 10, + "gross_built_up_area": 178596.000, + "built_form": 367 + } + }, + { + "pk": 1128, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.5719200000, + "net_built_up_area": 1785.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 1.0000000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 4.1000000000, + "building_use_definition": 3, + "gross_built_up_area": 178596.000, + "built_form": 367 + } + }, + { + "pk": 1129, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 150.8240640452, + "net_built_up_area": 128109.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 849.400, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.4600000000, + "building_use_definition": 2, + "gross_built_up_area": 150717.600, + "built_form": 368 + } + }, + { + "pk": 1130, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 150.8240640452, + "net_built_up_area": 128109.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 849.400, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.4600000000, + "building_use_definition": 14, + "gross_built_up_area": 150717.600, + "built_form": 368 + } + }, + { + "pk": 1131, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.9280000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 412.500, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 25, + "gross_built_up_area": 13068.000, + "built_form": 369 + } + }, + { + "pk": 1132, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.9280000000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 412.500, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 369 + } + }, + { + "pk": 1133, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.3519573608, + "net_built_up_area": 41382.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1632.300, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.9500000000, + "building_use_definition": 2, + "gross_built_up_area": 41382.000, + "built_form": 370 + } + }, + { + "pk": 1134, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 25.3519573608, + "net_built_up_area": 41382.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1632.300, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.9500000000, + "building_use_definition": 12, + "gross_built_up_area": 41382.000, + "built_form": 370 + } + }, + { + "pk": 1135, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.0910750000, + "net_built_up_area": 3818.306, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1250000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1031250000, + "building_use_definition": 9, + "gross_built_up_area": 4492.125, + "built_form": 371 + } + }, + { + "pk": 1136, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 61.7482072601, + "net_built_up_area": 26728.144, + "vacancy_rate": 0.000, + "square_feet_per_unit": 432.857, + "percent": 0.8750000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7218750000, + "building_use_definition": 25, + "gross_built_up_area": 31444.875, + "built_form": 371 + } + }, + { + "pk": 1137, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.0910750000, + "net_built_up_area": 3818.306, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1250000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1031250000, + "building_use_definition": 3, + "gross_built_up_area": 4492.125, + "built_form": 371 + } + }, + { + "pk": 1138, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 61.7482072601, + "net_built_up_area": 26728.144, + "vacancy_rate": 0.000, + "square_feet_per_unit": 432.857, + "percent": 0.8750000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.7218750000, + "building_use_definition": 5, + "gross_built_up_area": 31444.875, + "built_form": 371 + } + }, + { + "pk": 1139, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.4944000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 25, + "gross_built_up_area": 17424.000, + "built_form": 372 + } + }, + { + "pk": 1140, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.4944000000, + "net_built_up_area": 14810.400, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4000000000, + "building_use_definition": 5, + "gross_built_up_area": 17424.000, + "built_form": 372 + } + }, + { + "pk": 1141, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9605263158, + "net_built_up_area": 30886.841, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1940.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7090643000, + "building_use_definition": 2, + "gross_built_up_area": 30886.841, + "built_form": 373 + } + }, + { + "pk": 1142, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9605263158, + "net_built_up_area": 30886.841, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1940.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7090643000, + "building_use_definition": 26, + "gross_built_up_area": 30886.841, + "built_form": 373 + } + }, + { + "pk": 1143, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6365492000, + "net_built_up_area": 1636.549, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.1700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0442000000, + "building_use_definition": 4, + "gross_built_up_area": 1925.352, + "built_form": 374 + } + }, + { + "pk": 1144, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6365492000, + "net_built_up_area": 1636.549, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.1700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0442000000, + "building_use_definition": 18, + "gross_built_up_area": 1925.352, + "built_form": 374 + } + }, + { + "pk": 1145, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.2319591651, + "net_built_up_area": 7990.211, + "vacancy_rate": 0.000, + "square_feet_per_unit": 438.253, + "percent": 0.8300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2158000000, + "building_use_definition": 5, + "gross_built_up_area": 9400.248, + "built_form": 374 + } + }, + { + "pk": 1146, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.2319591651, + "net_built_up_area": 7990.211, + "vacancy_rate": 0.000, + "square_feet_per_unit": 438.253, + "percent": 0.8300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2158000000, + "building_use_definition": 15, + "gross_built_up_area": 9400.248, + "built_form": 374 + } + }, + { + "pk": 1147, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 43.0848000000, + "net_built_up_area": 22511.808, + "vacancy_rate": 0.000, + "square_feet_per_unit": 522.500, + "percent": 1.0000000, + "efficiency": 0.8075, + "household_size": 2.500, + "floor_area_ratio": 0.6400000000, + "building_use_definition": 3, + "gross_built_up_area": 27878.400, + "built_form": 375 + } + }, + { + "pk": 1148, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.9305600000, + "net_built_up_area": 22511.808, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6080000000, + "building_use_definition": 17, + "gross_built_up_area": 26484.480, + "built_form": 375 + } + }, + { + "pk": 1149, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1542400000, + "net_built_up_area": 1184.832, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0320000000, + "building_use_definition": 7, + "gross_built_up_area": 1393.920, + "built_form": 375 + } + }, + { + "pk": 1150, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.0286330049, + "net_built_up_area": 46827.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1948.800, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.0750000000, + "building_use_definition": 2, + "gross_built_up_area": 46827.000, + "built_form": 376 + } + }, + { + "pk": 1151, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.0286330049, + "net_built_up_area": 46827.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1948.800, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 1.0750000000, + "building_use_definition": 12, + "gross_built_up_area": 46827.000, + "built_form": 376 + } + }, + { + "pk": 1152, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 422.6443937276, + "net_built_up_area": 431554.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1021.081, + "percent": 0.9765000, + "efficiency": 0.8587, + "household_size": 2.442, + "floor_area_ratio": 11.5373475000, + "building_use_definition": 2, + "gross_built_up_area": 502566.857, + "built_form": 377 + } + }, + { + "pk": 1153, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.5919837152, + "net_built_up_area": 10460.107, + "vacancy_rate": 0.000, + "square_feet_per_unit": 484.444, + "percent": 0.0225000, + "efficiency": 0.9033, + "household_size": 2.500, + "floor_area_ratio": 0.2658375000, + "building_use_definition": 13, + "gross_built_up_area": 11579.881, + "built_form": 377 + } + }, + { + "pk": 1154, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.5526436558, + "net_built_up_area": 10460.570, + "vacancy_rate": 0.000, + "square_feet_per_unit": 463.829, + "percent": 0.0235000, + "efficiency": 0.8649, + "household_size": 2.500, + "floor_area_ratio": 0.2776525000, + "building_use_definition": 3, + "gross_built_up_area": 12094.543, + "built_form": 377 + } + }, + { + "pk": 1155, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 422.6443937276, + "net_built_up_area": 431554.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1021.081, + "percent": 0.9765000, + "efficiency": 0.8587, + "household_size": 2.442, + "floor_area_ratio": 11.5373475000, + "building_use_definition": 14, + "gross_built_up_area": 502566.857, + "built_form": 377 + } + }, + { + "pk": 1156, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9209730316, + "net_built_up_area": 437.462, + "vacancy_rate": 0.000, + "square_feet_per_unit": 475.000, + "percent": 0.0010000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0118150000, + "building_use_definition": 7, + "gross_built_up_area": 514.661, + "built_form": 377 + } + }, + { + "pk": 1157, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.9291844529, + "net_built_up_area": 35544.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 890.200, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9600000000, + "building_use_definition": 2, + "gross_built_up_area": 41817.600, + "built_form": 378 + } + }, + { + "pk": 1158, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 39.9291844529, + "net_built_up_area": 35544.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 890.200, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.9600000000, + "building_use_definition": 14, + "gross_built_up_area": 41817.600, + "built_form": 378 + } + }, + { + "pk": 1159, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0955931681, + "net_built_up_area": 1720.901, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3049.800, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0395064600, + "building_use_definition": 2, + "gross_built_up_area": 1720.901, + "built_form": 379 + } + }, + { + "pk": 1160, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0955931681, + "net_built_up_area": 1720.901, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3049.800, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0395064600, + "building_use_definition": 11, + "gross_built_up_area": 1720.901, + "built_form": 379 + } + }, + { + "pk": 1161, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 8, + "gross_built_up_area": 13068.000, + "built_form": 380 + } + }, + { + "pk": 1162, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4431200000, + "net_built_up_area": 11107.800, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2500.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3000000000, + "building_use_definition": 5, + "gross_built_up_area": 13068.000, + "built_form": 380 + } + }, + { + "pk": 1163, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3030.8645816461, + "net_built_up_area": 785909.248, + "vacancy_rate": 0.000, + "square_feet_per_unit": 259.302, + "percent": 0.5375000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 21.2258750000, + "building_use_definition": 5, + "gross_built_up_area": 924599.115, + "built_form": 381 + } + }, + { + "pk": 1164, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 70.9815306483, + "net_built_up_area": 38016.075, + "vacancy_rate": 0.000, + "square_feet_per_unit": 535.577, + "percent": 0.0260000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0267400000, + "building_use_definition": 7, + "gross_built_up_area": 44724.794, + "built_form": 381 + } + }, + { + "pk": 1165, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 317.5064271788, + "net_built_up_area": 638231.417, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2010.137, + "percent": 0.4365000, + "efficiency": 0.8500, + "household_size": 1.181, + "floor_area_ratio": 17.2373850000, + "building_use_definition": 2, + "gross_built_up_area": 750860.491, + "built_form": 381 + } + }, + { + "pk": 1166, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 317.5064271788, + "net_built_up_area": 638231.417, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2010.137, + "percent": 0.4365000, + "efficiency": 0.8500, + "household_size": 1.181, + "floor_area_ratio": 17.2373850000, + "building_use_definition": 14, + "gross_built_up_area": 750860.491, + "built_form": 381 + } + }, + { + "pk": 1167, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 70.9815306483, + "net_built_up_area": 38016.075, + "vacancy_rate": 0.000, + "square_feet_per_unit": 535.577, + "percent": 0.0260000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 1.0267400000, + "building_use_definition": 3, + "gross_built_up_area": 44724.794, + "built_form": 381 + } + }, + { + "pk": 1168, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3030.8645816461, + "net_built_up_area": 785909.248, + "vacancy_rate": 0.000, + "square_feet_per_unit": 259.302, + "percent": 0.5375000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 21.2258750000, + "building_use_definition": 15, + "gross_built_up_area": 924599.115, + "built_form": 381 + } + }, + { + "pk": 1169, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.6512161959, + "net_built_up_area": 4721.465, + "vacancy_rate": 0.000, + "square_feet_per_unit": 208.442, + "percent": 0.9595000, + "efficiency": 0.2215, + "household_size": 2.500, + "floor_area_ratio": 0.4893450000, + "building_use_definition": 4, + "gross_built_up_area": 21315.868, + "built_form": 382 + } + }, + { + "pk": 1170, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1850629429, + "net_built_up_area": 764.772, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0405000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0206550000, + "building_use_definition": 5, + "gross_built_up_area": 899.732, + "built_form": 382 + } + }, + { + "pk": 1171, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.2487335000, + "net_built_up_area": 3398.987, + "vacancy_rate": 0.000, + "square_feet_per_unit": 800.000, + "percent": 0.1800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0918000000, + "building_use_definition": 19, + "gross_built_up_area": 3998.808, + "built_form": 382 + } + }, + { + "pk": 1172, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.3247203219, + "net_built_up_area": 9998.686, + "vacancy_rate": 0.000, + "square_feet_per_unit": 882.908, + "percent": 0.5295000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2700450000, + "building_use_definition": 20, + "gross_built_up_area": 11763.160, + "built_form": 382 + } + }, + { + "pk": 1173, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9010187500, + "net_built_up_area": 4720.815, + "vacancy_rate": 0.000, + "square_feet_per_unit": 800.000, + "percent": 0.2500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1275000000, + "building_use_definition": 18, + "gross_built_up_area": 5553.900, + "built_form": 382 + } + }, + { + "pk": 1174, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1850629429, + "net_built_up_area": 764.772, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.0405000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0206550000, + "building_use_definition": 15, + "gross_built_up_area": 899.732, + "built_form": 382 + } + }, + { + "pk": 1175, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 25, + "gross_built_up_area": 8712.000, + "built_form": 383 + } + }, + { + "pk": 1176, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.7472000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 375.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 8712.000, + "built_form": 383 + } + }, + { + "pk": 1177, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6928000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 8, + "gross_built_up_area": 8712.000, + "built_form": 384 + } + }, + { + "pk": 1178, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6928000000, + "net_built_up_area": 7405.200, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2000000000, + "building_use_definition": 5, + "gross_built_up_area": 8712.000, + "built_form": 384 + } + }, + { + "pk": 1179, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.3733321856, + "net_built_up_area": 33759.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1742.550, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7750000000, + "building_use_definition": 2, + "gross_built_up_area": 33759.000, + "built_form": 385 + } + }, + { + "pk": 1180, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.3733321856, + "net_built_up_area": 33759.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1742.550, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.7750000000, + "building_use_definition": 12, + "gross_built_up_area": 33759.000, + "built_form": 385 + } + }, + { + "pk": 1181, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0002591820, + "net_built_up_area": 2.592, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.0500000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0000595000, + "building_use_definition": 23, + "gross_built_up_area": 2.592, + "built_form": 386 + } + }, + { + "pk": 1182, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0051550024, + "net_built_up_area": 48.958, + "vacancy_rate": 0.000, + "square_feet_per_unit": 9497.235, + "percent": 0.9945000, + "efficiency": 0.9497, + "household_size": 2.500, + "floor_area_ratio": 0.0011834550, + "building_use_definition": 6, + "gross_built_up_area": 51.551, + "built_form": 386 + } + }, + { + "pk": 1183, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.0014144272, + "net_built_up_area": 0.242, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0055000, + "efficiency": 0.8500, + "household_size": 0.050, + "floor_area_ratio": 0.0000065450, + "building_use_definition": 11, + "gross_built_up_area": 0.285, + "built_form": 386 + } + }, + { + "pk": 1184, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0048959480, + "net_built_up_area": 48.959, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.9445000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0011239550, + "building_use_definition": 22, + "gross_built_up_area": 48.959, + "built_form": 386 + } + }, + { + "pk": 1185, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.0014144272, + "net_built_up_area": 0.242, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0055000, + "efficiency": 0.8500, + "household_size": 0.050, + "floor_area_ratio": 0.0000065450, + "building_use_definition": 2, + "gross_built_up_area": 0.285, + "built_form": 386 + } + }, + { + "pk": 1186, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9052857000, + "net_built_up_area": 452.643, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0150000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0122250000, + "building_use_definition": 9, + "gross_built_up_area": 532.521, + "built_form": 387 + } + }, + { + "pk": 1187, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9052857000, + "net_built_up_area": 452.643, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0150000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0122250000, + "building_use_definition": 3, + "gross_built_up_area": 532.521, + "built_form": 387 + } + }, + { + "pk": 1188, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 92.7976370959, + "net_built_up_area": 29723.547, + "vacancy_rate": 0.000, + "square_feet_per_unit": 320.305, + "percent": 0.9850000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8027750000, + "building_use_definition": 5, + "gross_built_up_area": 34968.879, + "built_form": 387 + } + }, + { + "pk": 1189, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 92.7976370959, + "net_built_up_area": 29723.547, + "vacancy_rate": 0.000, + "square_feet_per_unit": 320.305, + "percent": 0.9850000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.8027750000, + "building_use_definition": 15, + "gross_built_up_area": 34968.879, + "built_form": 387 + } + }, + { + "pk": 1190, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2838660000, + "net_built_up_area": 85.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.0050000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0023000000, + "building_use_definition": 5, + "gross_built_up_area": 100.188, + "built_form": 388 + } + }, + { + "pk": 1191, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.5418560000, + "net_built_up_area": 3406.392, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0920000000, + "building_use_definition": 7, + "gross_built_up_area": 4007.520, + "built_form": 388 + } + }, + { + "pk": 1192, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.7121756000, + "net_built_up_area": 3534.132, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.2075000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0954500000, + "building_use_definition": 10, + "gross_built_up_area": 4157.802, + "built_form": 388 + } + }, + { + "pk": 1193, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.3417020000, + "net_built_up_area": 10006.277, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.5875000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2702500000, + "building_use_definition": 9, + "gross_built_up_area": 11772.090, + "built_form": 388 + } + }, + { + "pk": 1194, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.5964449446, + "net_built_up_area": 10006.587, + "vacancy_rate": 0.000, + "square_feet_per_unit": 442.839, + "percent": 0.9950000, + "efficiency": 0.5019, + "household_size": 2.500, + "floor_area_ratio": 0.4577000000, + "building_use_definition": 3, + "gross_built_up_area": 19937.412, + "built_form": 388 + } + }, + { + "pk": 1195, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2838660000, + "net_built_up_area": 85.160, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.0050000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0023000000, + "building_use_definition": 15, + "gross_built_up_area": 100.188, + "built_form": 388 + } + }, + { + "pk": 1196, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 24.4393561984, + "net_built_up_area": 10997.710, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.1040000, + "efficiency": 0.9446, + "household_size": 2.500, + "floor_area_ratio": 0.2672800000, + "building_use_definition": 9, + "gross_built_up_area": 11642.717, + "built_form": 389 + } + }, + { + "pk": 1197, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 90.0786345416, + "net_built_up_area": 62040.579, + "vacancy_rate": 0.000, + "square_feet_per_unit": 688.738, + "percent": 0.7490000, + "efficiency": 0.7399, + "household_size": 1.881, + "floor_area_ratio": 1.9249300000, + "building_use_definition": 2, + "gross_built_up_area": 83849.951, + "built_form": 389 + } + }, + { + "pk": 1198, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 58.9853054205, + "net_built_up_area": 10998.046, + "vacancy_rate": 0.000, + "square_feet_per_unit": 186.454, + "percent": 0.2510000, + "efficiency": 0.3914, + "household_size": 2.500, + "floor_area_ratio": 0.6450700000, + "building_use_definition": 3, + "gross_built_up_area": 28099.249, + "built_form": 389 + } + }, + { + "pk": 1199, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 90.0786345416, + "net_built_up_area": 62040.579, + "vacancy_rate": 0.000, + "square_feet_per_unit": 688.738, + "percent": 0.7490000, + "efficiency": 0.7399, + "household_size": 1.881, + "floor_area_ratio": 1.9249300000, + "building_use_definition": 14, + "gross_built_up_area": 83849.951, + "built_form": 389 + } + }, + { + "pk": 1200, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 36.1418930621, + "net_built_up_area": 16122.465, + "vacancy_rate": 0.000, + "square_feet_per_unit": 446.088, + "percent": 0.1470000, + "efficiency": 0.9797, + "household_size": 2.500, + "floor_area_ratio": 0.3777900000, + "building_use_definition": 7, + "gross_built_up_area": 16456.532, + "built_form": 389 + } + }, + { + "pk": 1201, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.5522857143, + "net_built_up_area": 17031.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 420.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4600000000, + "building_use_definition": 25, + "gross_built_up_area": 20037.600, + "built_form": 390 + } + }, + { + "pk": 1202, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.5522857143, + "net_built_up_area": 17031.960, + "vacancy_rate": 0.000, + "square_feet_per_unit": 420.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4600000000, + "building_use_definition": 5, + "gross_built_up_area": 20037.600, + "built_form": 390 + } + }, + { + "pk": 1203, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.2156000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 10, + "gross_built_up_area": 19602.000, + "built_form": 391 + } + }, + { + "pk": 1204, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.2156000000, + "net_built_up_area": 16661.700, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4500000000, + "building_use_definition": 3, + "gross_built_up_area": 19602.000, + "built_form": 391 + } + }, + { + "pk": 1205, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5924160000, + "net_built_up_area": 444.312, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0120000000, + "building_use_definition": 9, + "gross_built_up_area": 522.720, + "built_form": 392 + } + }, + { + "pk": 1206, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 35.1747000000, + "net_built_up_area": 21104.820, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5700000000, + "building_use_definition": 8, + "gross_built_up_area": 24829.200, + "built_form": 392 + } + }, + { + "pk": 1207, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4810400000, + "net_built_up_area": 444.312, + "vacancy_rate": 0.000, + "square_feet_per_unit": 300.000, + "percent": 0.0500000, + "efficiency": 0.3400, + "household_size": 2.500, + "floor_area_ratio": 0.0300000000, + "building_use_definition": 3, + "gross_built_up_area": 1306.800, + "built_form": 392 + } + }, + { + "pk": 1208, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 35.1747000000, + "net_built_up_area": 21104.820, + "vacancy_rate": 0.000, + "square_feet_per_unit": 600.000, + "percent": 0.9500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.5700000000, + "building_use_definition": 5, + "gross_built_up_area": 24829.200, + "built_form": 392 + } + }, + { + "pk": 1209, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8886240000, + "net_built_up_area": 666.468, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0180000000, + "building_use_definition": 7, + "gross_built_up_area": 784.080, + "built_form": 392 + } + }, + { + "pk": 1210, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 34.2774154131, + "net_built_up_area": 5332.058, + "vacancy_rate": 0.000, + "square_feet_per_unit": 155.556, + "percent": 0.2700000, + "efficiency": 0.3778, + "household_size": 2.500, + "floor_area_ratio": 0.3240000000, + "building_use_definition": 5, + "gross_built_up_area": 14113.440, + "built_form": 393 + } + }, + { + "pk": 1211, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 29.6208000000, + "net_built_up_area": 15550.920, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.3500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4200000000, + "building_use_definition": 7, + "gross_built_up_area": 18295.200, + "built_form": 393 + } + }, + { + "pk": 1212, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 12.6946285714, + "net_built_up_area": 6664.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.1500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1800000000, + "building_use_definition": 16, + "gross_built_up_area": 7840.800, + "built_form": 393 + } + }, + { + "pk": 1213, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 32.1597257143, + "net_built_up_area": 16883.856, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.3800000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4560000000, + "building_use_definition": 9, + "gross_built_up_area": 19863.360, + "built_form": 393 + } + }, + { + "pk": 1214, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 61.7852331606, + "net_built_up_area": 16885.163, + "vacancy_rate": 0.000, + "square_feet_per_unit": 273.288, + "percent": 0.7300000, + "efficiency": 0.4425, + "household_size": 2.500, + "floor_area_ratio": 0.8760000000, + "building_use_definition": 3, + "gross_built_up_area": 38158.560, + "built_form": 393 + } + }, + { + "pk": 1215, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.2335542857, + "net_built_up_area": 5331.744, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.000, + "percent": 0.1200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1440000000, + "building_use_definition": 15, + "gross_built_up_area": 6272.640, + "built_form": 393 + } + }, + { + "pk": 1216, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.3359185920, + "net_built_up_area": 4667.959, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.2600000, + "efficiency": 0.0896, + "household_size": 2.500, + "floor_area_ratio": 1.1960000000, + "building_use_definition": 9, + "gross_built_up_area": 52097.760, + "built_form": 394 + } + }, + { + "pk": 1217, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.1144206240, + "net_built_up_area": 10557.210, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.5900000, + "efficiency": 0.0893, + "household_size": 2.500, + "floor_area_ratio": 2.7140000000, + "building_use_definition": 10, + "gross_built_up_area": 118221.840, + "built_form": 394 + } + }, + { + "pk": 1218, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 30.5134466232, + "net_built_up_area": 4666.757, + "vacancy_rate": 0.000, + "square_feet_per_unit": 152.941, + "percent": 0.8500000, + "efficiency": 0.0274, + "household_size": 2.500, + "floor_area_ratio": 3.9100000000, + "building_use_definition": 3, + "gross_built_up_area": 170319.600, + "built_form": 394 + } + }, + { + "pk": 1219, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.4500571429, + "net_built_up_area": 6011.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.1500000, + "efficiency": 0.2000, + "household_size": 2.500, + "floor_area_ratio": 0.6900000000, + "building_use_definition": 5, + "gross_built_up_area": 30056.400, + "built_form": 394 + } + }, + { + "pk": 1220, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.4500571429, + "net_built_up_area": 6011.280, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.1500000, + "efficiency": 0.2000, + "household_size": 2.500, + "floor_area_ratio": 0.6900000000, + "building_use_definition": 15, + "gross_built_up_area": 30056.400, + "built_form": 394 + } + }, + { + "pk": 1221, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.1842360000, + "net_built_up_area": 3821.083, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 0.2000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1032000000, + "building_use_definition": 19, + "gross_built_up_area": 4495.392, + "built_form": 395 + } + }, + { + "pk": 1222, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.9145764630, + "net_built_up_area": 15284.333, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3110.000, + "percent": 1.0000000, + "efficiency": 0.6800, + "household_size": 2.500, + "floor_area_ratio": 0.5160000000, + "building_use_definition": 4, + "gross_built_up_area": 22476.960, + "built_form": 395 + } + }, + { + "pk": 1223, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9316611704, + "net_built_up_area": 15284.333, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3887.500, + "percent": 0.8000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.4128000000, + "building_use_definition": 20, + "gross_built_up_area": 17981.568, + "built_form": 395 + } + }, + { + "pk": 1224, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1486493613, + "net_built_up_area": 23220.326, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4072.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5330653400, + "building_use_definition": 2, + "gross_built_up_area": 23220.326, + "built_form": 396 + } + }, + { + "pk": 1225, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1486493613, + "net_built_up_area": 23220.326, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4072.000, + "percent": 1.0000000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.5330653400, + "building_use_definition": 11, + "gross_built_up_area": 23220.326, + "built_form": 396 + } + }, + { + "pk": 1226, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 177.7248000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 5, + "gross_built_up_area": 130680.000, + "built_form": 397 + } + }, + { + "pk": 1227, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 177.7248000000, + "net_built_up_area": 111078.000, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 1.0000000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 3.0000000000, + "building_use_definition": 16, + "gross_built_up_area": 130680.000, + "built_form": 397 + } + }, + { + "pk": 1228, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 174.7927923921, + "net_built_up_area": 32326.004, + "vacancy_rate": 0.000, + "square_feet_per_unit": 184.939, + "percent": 0.1464587, + "efficiency": 0.6229, + "household_size": 2.500, + "floor_area_ratio": 1.1913677056, + "building_use_definition": 5, + "gross_built_up_area": 51895.977, + "built_form": 398 + } + }, + { + "pk": 1229, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.5948548519, + "net_built_up_area": 3761.739, + "vacancy_rate": 0.000, + "square_feet_per_unit": 495.301, + "percent": 0.0120502, + "efficiency": 0.8810, + "household_size": 2.500, + "floor_area_ratio": 0.0980223034, + "building_use_definition": 7, + "gross_built_up_area": 4269.852, + "built_form": 398 + } + }, + { + "pk": 1230, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 99.6524481957, + "net_built_up_area": 115865.902, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1162.700, + "percent": 0.3794291, + "efficiency": 0.8618, + "household_size": 2.206, + "floor_area_ratio": 3.0864644866, + "building_use_definition": 2, + "gross_built_up_area": 134446.393, + "built_form": 398 + } + }, + { + "pk": 1231, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.7473195816, + "net_built_up_area": 824.188, + "vacancy_rate": 0.000, + "square_feet_per_unit": 471.687, + "percent": 0.0025580, + "efficiency": 0.9093, + "household_size": 2.500, + "floor_area_ratio": 0.0208080407, + "building_use_definition": 9, + "gross_built_up_area": 906.398, + "built_form": 398 + } + }, + { + "pk": 1232, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.4089741408, + "net_built_up_area": 1647.305, + "vacancy_rate": 0.000, + "square_feet_per_unit": 483.226, + "percent": 0.0052212, + "efficiency": 0.8904, + "household_size": 2.500, + "floor_area_ratio": 0.0424718304, + "building_use_definition": 13, + "gross_built_up_area": 1850.073, + "built_form": 398 + } + }, + { + "pk": 1233, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 99.6524481957, + "net_built_up_area": 115865.902, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1162.700, + "percent": 0.3794291, + "efficiency": 0.8618, + "household_size": 2.206, + "floor_area_ratio": 3.0864644866, + "building_use_definition": 14, + "gross_built_up_area": 134446.393, + "built_form": 398 + } + }, + { + "pk": 1234, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.8190055672, + "net_built_up_area": 3011.878, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0813449597, + "building_use_definition": 16, + "gross_built_up_area": 3543.386, + "built_form": 398 + } + }, + { + "pk": 1235, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9304766753, + "net_built_up_area": 9035.635, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3083.333, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.2440348792, + "building_use_definition": 8, + "gross_built_up_area": 10630.159, + "built_form": 398 + } + }, + { + "pk": 1236, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.1996450494, + "net_built_up_area": 2388.305, + "vacancy_rate": 0.000, + "square_feet_per_unit": 157.129, + "percent": 0.0277602, + "efficiency": 0.2428, + "household_size": 2.500, + "floor_area_ratio": 0.2258152352, + "building_use_definition": 3, + "gross_built_up_area": 9836.512, + "built_form": 398 + } + }, + { + "pk": 1237, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.3430192446, + "net_built_up_area": 2388.661, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0079308, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0645130607, + "building_use_definition": 17, + "gross_built_up_area": 2810.189, + "built_form": 398 + } + }, + { + "pk": 1238, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 127.0624728077, + "net_built_up_area": 32328.124, + "vacancy_rate": 0.000, + "square_feet_per_unit": 254.427, + "percent": 0.1064587, + "efficiency": 0.8570, + "household_size": 2.500, + "floor_area_ratio": 0.8659878666, + "building_use_definition": 15, + "gross_built_up_area": 37722.431, + "built_form": 398 + } + }, + { + "pk": 1239, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.1326092349, + "net_built_up_area": 10002.429, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1948.800, + "percent": 0.0513648, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2296241707, + "building_use_definition": 12, + "gross_built_up_area": 10002.429, + "built_form": 399 + } + }, + { + "pk": 1240, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 27.6868453591, + "net_built_up_area": 522.728, + "vacancy_rate": 0.000, + "square_feet_per_unit": 18.880, + "percent": 0.0431564, + "efficiency": 0.0622, + "household_size": 2.500, + "floor_area_ratio": 0.1929288649, + "building_use_definition": 5, + "gross_built_up_area": 8403.981, + "built_form": 399 + } + }, + { + "pk": 1241, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.6540964916, + "net_built_up_area": 1848.863, + "vacancy_rate": 0.000, + "square_feet_per_unit": 505.970, + "percent": 0.0105940, + "efficiency": 0.8962, + "household_size": 2.500, + "floor_area_ratio": 0.0473600299, + "building_use_definition": 7, + "gross_built_up_area": 2063.003, + "built_form": 399 + } + }, + { + "pk": 1242, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 83.7824043850, + "net_built_up_area": 69745.668, + "vacancy_rate": 0.000, + "square_feet_per_unit": 832.462, + "percent": 0.4738196, + "efficiency": 0.7559, + "household_size": 2.400, + "floor_area_ratio": 2.1181905259, + "building_use_definition": 2, + "gross_built_up_area": 92268.379, + "built_form": 399 + } + }, + { + "pk": 1243, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.0918153254, + "net_built_up_area": 491.317, + "vacancy_rate": 0.000, + "square_feet_per_unit": 450.000, + "percent": 0.0026710, + "efficiency": 0.9446, + "household_size": 2.500, + "floor_area_ratio": 0.0119405928, + "building_use_definition": 9, + "gross_built_up_area": 520.132, + "built_form": 399 + } + }, + { + "pk": 1244, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.2985806016, + "net_built_up_area": 628.703, + "vacancy_rate": 0.000, + "square_feet_per_unit": 484.146, + "percent": 0.0035801, + "efficiency": 0.9018, + "household_size": 2.500, + "floor_area_ratio": 0.0160046860, + "building_use_definition": 13, + "gross_built_up_area": 697.164, + "built_form": 399 + } + }, + { + "pk": 1245, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 74.6992783119, + "net_built_up_area": 69745.073, + "vacancy_rate": 0.000, + "square_feet_per_unit": 933.678, + "percent": 0.4224548, + "efficiency": 0.8478, + "household_size": 2.388, + "floor_area_ratio": 1.8885663552, + "building_use_definition": 14, + "gross_built_up_area": 82265.950, + "built_form": 399 + } + }, + { + "pk": 1246, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.2539325337, + "net_built_up_area": 6620.927, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2937.500, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1788183119, + "building_use_definition": 8, + "gross_built_up_area": 7789.326, + "built_form": 399 + } + }, + { + "pk": 1247, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.0375009548, + "net_built_up_area": 3282.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 297.360, + "percent": 0.0366719, + "efficiency": 0.4596, + "household_size": 2.500, + "floor_area_ratio": 0.1639401813, + "building_use_definition": 3, + "gross_built_up_area": 7141.234, + "built_form": 399 + } + }, + { + "pk": 1248, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9668996288, + "net_built_up_area": 3281.795, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0198268, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0886348727, + "building_use_definition": 17, + "gross_built_up_area": 3860.935, + "built_form": 399 + } + }, + { + "pk": 1249, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.0239615366, + "net_built_up_area": 522.457, + "vacancy_rate": 0.000, + "square_feet_per_unit": 258.136, + "percent": 0.0031564, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0141105530, + "building_use_definition": 15, + "gross_built_up_area": 614.656, + "built_form": 399 + } + }, + { + "pk": 1250, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 167.5722882005, + "net_built_up_area": 45256.751, + "vacancy_rate": 0.000, + "square_feet_per_unit": 270.073, + "percent": 0.3640723, + "efficiency": 0.8040, + "household_size": 2.500, + "floor_area_ratio": 1.2922288977, + "building_use_definition": 5, + "gross_built_up_area": 56289.491, + "built_form": 400 + } + }, + { + "pk": 1251, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.8332258817, + "net_built_up_area": 5065.921, + "vacancy_rate": 0.000, + "square_feet_per_unit": 515.184, + "percent": 0.0378531, + "efficiency": 0.8656, + "household_size": 2.500, + "floor_area_ratio": 0.1343548237, + "building_use_definition": 7, + "gross_built_up_area": 5852.496, + "built_form": 400 + } + }, + { + "pk": 1252, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.9754410626, + "net_built_up_area": 7398.815, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1060.695, + "percent": 0.0590723, + "efficiency": 0.8101, + "household_size": 1.162, + "floor_area_ratio": 0.2096697088, + "building_use_definition": 2, + "gross_built_up_area": 9133.213, + "built_form": 400 + } + }, + { + "pk": 1253, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4931177165, + "net_built_up_area": 246.559, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0323470, + "efficiency": 0.0493, + "household_size": 2.500, + "floor_area_ratio": 0.1148116134, + "building_use_definition": 10, + "gross_built_up_area": 5001.194, + "built_form": 400 + } + }, + { + "pk": 1254, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4095196132, + "net_built_up_area": 203.174, + "vacancy_rate": 0.000, + "square_feet_per_unit": 496.127, + "percent": 0.0015440, + "efficiency": 0.8511, + "household_size": 2.500, + "floor_area_ratio": 0.0054802341, + "building_use_definition": 13, + "gross_built_up_area": 238.719, + "built_form": 400 + } + }, + { + "pk": 1255, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.9754410626, + "net_built_up_area": 7398.815, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1060.695, + "percent": 0.0590723, + "efficiency": 0.8101, + "household_size": 1.162, + "floor_area_ratio": 0.2096697088, + "building_use_definition": 14, + "gross_built_up_area": 9133.213, + "built_form": 400 + } + }, + { + "pk": 1256, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.1129960787, + "net_built_up_area": 2385.875, + "vacancy_rate": 0.000, + "square_feet_per_unit": 580.082, + "percent": 0.0181547, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0644378272, + "building_use_definition": 16, + "gross_built_up_area": 2806.912, + "built_form": 400 + } + }, + { + "pk": 1257, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.1799727226, + "net_built_up_area": 4173.488, + "vacancy_rate": 0.000, + "square_feet_per_unit": 510.208, + "percent": 0.0377743, + "efficiency": 0.7146, + "household_size": 2.500, + "floor_area_ratio": 0.1340751330, + "building_use_definition": 9, + "gross_built_up_area": 5840.313, + "built_form": 400 + } + }, + { + "pk": 1258, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 31.1875043860, + "net_built_up_area": 2758.223, + "vacancy_rate": 0.000, + "square_feet_per_unit": 88.440, + "percent": 0.1305032, + "efficiency": 0.1367, + "household_size": 2.500, + "floor_area_ratio": 0.4632047159, + "building_use_definition": 3, + "gross_built_up_area": 20177.197, + "built_form": 400 + } + }, + { + "pk": 1259, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.0141896156, + "net_built_up_area": 2757.804, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0209848, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0744829117, + "building_use_definition": 17, + "gross_built_up_area": 3244.476, + "built_form": 400 + } + }, + { + "pk": 1260, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 159.2170132865, + "net_built_up_area": 45256.958, + "vacancy_rate": 0.000, + "square_feet_per_unit": 284.247, + "percent": 0.3459176, + "efficiency": 0.8462, + "household_size": 2.500, + "floor_area_ratio": 1.2277910705, + "building_use_definition": 15, + "gross_built_up_area": 53482.579, + "built_form": 400 + } + }, + { + "pk": 1261, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2726749762, + "net_built_up_area": 194.926, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0027336, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0052645727, + "building_use_definition": 21, + "gross_built_up_area": 229.325, + "built_form": 401 + } + }, + { + "pk": 1262, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 49.6534962049, + "net_built_up_area": 10639.354, + "vacancy_rate": 0.000, + "square_feet_per_unit": 214.272, + "percent": 0.2047850, + "efficiency": 0.6193, + "household_size": 2.500, + "floor_area_ratio": 0.3943903733, + "building_use_definition": 5, + "gross_built_up_area": 17179.645, + "built_form": 401 + } + }, + { + "pk": 1263, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9169492809, + "net_built_up_area": 1953.962, + "vacancy_rate": 0.000, + "square_feet_per_unit": 498.848, + "percent": 0.0262589, + "efficiency": 0.8870, + "household_size": 2.500, + "floor_area_ratio": 0.0505713669, + "building_use_definition": 7, + "gross_built_up_area": 2202.889, + "built_form": 401 + } + }, + { + "pk": 1264, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1954266890, + "net_built_up_area": 4278.448, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1948.800, + "percent": 0.0510000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0982196403, + "building_use_definition": 12, + "gross_built_up_area": 4278.448, + "built_form": 401 + } + }, + { + "pk": 1265, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.8086988303, + "net_built_up_area": 14772.793, + "vacancy_rate": 0.000, + "square_feet_per_unit": 745.773, + "percent": 0.2667295, + "efficiency": 0.6602, + "household_size": 1.972, + "floor_area_ratio": 0.5136877558, + "building_use_definition": 2, + "gross_built_up_area": 22376.239, + "built_form": 401 + } + }, + { + "pk": 1266, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6166519266, + "net_built_up_area": 416.748, + "vacancy_rate": 0.000, + "square_feet_per_unit": 675.824, + "percent": 0.0139230, + "efficiency": 0.3568, + "household_size": 2.500, + "floor_area_ratio": 0.0268139618, + "building_use_definition": 10, + "gross_built_up_area": 1168.016, + "built_form": 401 + } + }, + { + "pk": 1267, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.3503933307, + "net_built_up_area": 2357.678, + "vacancy_rate": 0.000, + "square_feet_per_unit": 541.946, + "percent": 0.0361095, + "efficiency": 0.7783, + "household_size": 2.500, + "floor_area_ratio": 0.0695423941, + "building_use_definition": 9, + "gross_built_up_area": 3029.267, + "built_form": 401 + } + }, + { + "pk": 1268, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0545502060, + "net_built_up_area": 27.275, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0003825, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0007366473, + "building_use_definition": 13, + "gross_built_up_area": 32.088, + "built_form": 401 + } + }, + { + "pk": 1269, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.8171704546, + "net_built_up_area": 14771.861, + "vacancy_rate": 0.000, + "square_feet_per_unit": 933.913, + "percent": 0.2129959, + "efficiency": 0.8267, + "household_size": 1.862, + "floor_area_ratio": 0.4102035428, + "building_use_definition": 14, + "gross_built_up_area": 17868.466, + "built_form": 401 + } + }, + { + "pk": 1270, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6503786309, + "net_built_up_area": 985.826, + "vacancy_rate": 0.000, + "square_feet_per_unit": 597.333, + "percent": 0.0138250, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0266252260, + "building_use_definition": 16, + "gross_built_up_area": 1159.795, + "built_form": 401 + } + }, + { + "pk": 1271, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9709951844, + "net_built_up_area": 2852.298, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2937.500, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0770350120, + "building_use_definition": 8, + "gross_built_up_area": 3355.645, + "built_form": 401 + } + }, + { + "pk": 1272, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.4759162706, + "net_built_up_area": 842.527, + "vacancy_rate": 0.000, + "square_feet_per_unit": 73.417, + "percent": 0.0884855, + "efficiency": 0.1135, + "household_size": 2.500, + "floor_area_ratio": 0.1704120389, + "building_use_definition": 3, + "gross_built_up_area": 7423.148, + "built_form": 401 + } + }, + { + "pk": 1273, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.5313730564, + "net_built_up_area": 842.255, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0118116, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0227476687, + "building_use_definition": 17, + "gross_built_up_area": 990.888, + "built_form": 401 + } + }, + { + "pk": 1274, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 36.6022009857, + "net_built_up_area": 10639.198, + "vacancy_rate": 0.000, + "square_feet_per_unit": 290.671, + "percent": 0.1509600, + "efficiency": 0.8401, + "household_size": 2.500, + "floor_area_ratio": 0.2907301353, + "building_use_definition": 15, + "gross_built_up_area": 12664.205, + "built_form": 401 + } + }, + { + "pk": 1275, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.2913282104, + "net_built_up_area": 536.416, + "vacancy_rate": 0.000, + "square_feet_per_unit": 125.000, + "percent": 0.0500000, + "efficiency": 0.1700, + "household_size": 2.500, + "floor_area_ratio": 0.0724377500, + "building_use_definition": 5, + "gross_built_up_area": 3155.388, + "built_form": 402 + } + }, + { + "pk": 1276, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.0256095894, + "net_built_up_area": 733.173, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0136680, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0198015833, + "building_use_definition": 21, + "gross_built_up_area": 862.557, + "built_form": 402 + } + }, + { + "pk": 1277, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4536324039, + "net_built_up_area": 249.498, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0046512, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0067384493, + "building_use_definition": 7, + "gross_built_up_area": 293.527, + "built_form": 402 + } + }, + { + "pk": 1278, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9454978284, + "net_built_up_area": 11586.586, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1948.800, + "percent": 0.1836000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2659914180, + "building_use_definition": 12, + "gross_built_up_area": 11586.586, + "built_form": 402 + } + }, + { + "pk": 1279, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.7523368156, + "net_built_up_area": 14532.431, + "vacancy_rate": 0.000, + "square_feet_per_unit": 543.221, + "percent": 0.4693836, + "efficiency": 0.4906, + "household_size": 2.381, + "floor_area_ratio": 0.6800218374, + "building_use_definition": 2, + "gross_built_up_area": 29621.751, + "built_form": 402 + } + }, + { + "pk": 1280, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0875430955, + "net_built_up_area": 87.543, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0016320, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0023643682, + "building_use_definition": 10, + "gross_built_up_area": 102.992, + "built_form": 402 + } + }, + { + "pk": 1281, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8466321875, + "net_built_up_area": 996.897, + "vacancy_rate": 0.000, + "square_feet_per_unit": 539.846, + "percent": 0.0185844, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0269242424, + "building_use_definition": 9, + "gross_built_up_area": 1172.820, + "built_form": 402 + } + }, + { + "pk": 1282, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 15.5099151406, + "net_built_up_area": 14533.178, + "vacancy_rate": 0.000, + "square_feet_per_unit": 937.025, + "percent": 0.2721156, + "efficiency": 0.8463, + "household_size": 2.384, + "floor_area_ratio": 0.3942288361, + "building_use_definition": 14, + "gross_built_up_area": 17172.608, + "built_form": 402 + } + }, + { + "pk": 1283, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8582656421, + "net_built_up_area": 536.416, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0144875500, + "building_use_definition": 16, + "gross_built_up_area": 631.078, + "built_form": 402 + } + }, + { + "pk": 1284, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7304388443, + "net_built_up_area": 2145.664, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2937.500, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0579502000, + "building_use_definition": 8, + "gross_built_up_area": 2524.311, + "built_form": 402 + } + }, + { + "pk": 1285, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.9615217485, + "net_built_up_area": 844.834, + "vacancy_rate": 0.000, + "square_feet_per_unit": 213.260, + "percent": 0.0406164, + "efficiency": 0.3296, + "household_size": 2.500, + "floor_area_ratio": 0.0588432126, + "building_use_definition": 3, + "gross_built_up_area": 2563.210, + "built_form": 402 + } + }, + { + "pk": 1286, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.5359834027, + "net_built_up_area": 844.791, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0157488, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0228161527, + "building_use_definition": 17, + "gross_built_up_area": 993.872, + "built_form": 402 + } + }, + { + "pk": 1287, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 43.2341805814, + "net_built_up_area": 11412.527, + "vacancy_rate": 0.000, + "square_feet_per_unit": 263.970, + "percent": 0.2587925, + "efficiency": 0.6887, + "household_size": 2.500, + "floor_area_ratio": 0.3804204461, + "building_use_definition": 5, + "gross_built_up_area": 16571.115, + "built_form": 403 + } + }, + { + "pk": 1288, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1222556562, + "net_built_up_area": 802.262, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0147400, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0216675420, + "building_use_definition": 21, + "gross_built_up_area": 943.838, + "built_form": 403 + } + }, + { + "pk": 1289, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.5870549235, + "net_built_up_area": 4507.637, + "vacancy_rate": 0.000, + "square_feet_per_unit": 524.934, + "percent": 0.0828190, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1217424807, + "building_use_definition": 7, + "gross_built_up_area": 5303.102, + "built_form": 403 + } + }, + { + "pk": 1290, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4967582312, + "net_built_up_area": 694.496, + "vacancy_rate": 0.000, + "square_feet_per_unit": 464.000, + "percent": 0.0275000, + "efficiency": 0.3944, + "household_size": 0.424, + "floor_area_ratio": 0.0404245187, + "building_use_definition": 2, + "gross_built_up_area": 1760.892, + "built_form": 403 + } + }, + { + "pk": 1291, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4434996771, + "net_built_up_area": 221.750, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0631950, + "efficiency": 0.0548, + "household_size": 2.500, + "floor_area_ratio": 0.0928955441, + "building_use_definition": 10, + "gross_built_up_area": 4046.530, + "built_form": 403 + } + }, + { + "pk": 1292, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0868119774, + "net_built_up_area": 43.406, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0007975, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0011723110, + "building_use_definition": 13, + "gross_built_up_area": 51.066, + "built_form": 403 + } + }, + { + "pk": 1293, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6944958193, + "net_built_up_area": 694.496, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0127600, + "efficiency": 0.8500, + "household_size": 0.080, + "floor_area_ratio": 0.0187569767, + "building_use_definition": 14, + "gross_built_up_area": 817.054, + "built_form": 403 + } + }, + { + "pk": 1294, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.1834095744, + "net_built_up_area": 2295.483, + "vacancy_rate": 0.000, + "square_feet_per_unit": 548.711, + "percent": 0.0421750, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0619965119, + "building_use_definition": 16, + "gross_built_up_area": 2700.568, + "built_form": 403 + } + }, + { + "pk": 1295, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.4652632132, + "net_built_up_area": 5447.274, + "vacancy_rate": 0.000, + "square_feet_per_unit": 520.510, + "percent": 0.1141580, + "efficiency": 0.7452, + "household_size": 2.500, + "floor_area_ratio": 0.1678102622, + "building_use_definition": 9, + "gross_built_up_area": 7309.815, + "built_form": 403 + } + }, + { + "pk": 1296, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 27.1150130604, + "net_built_up_area": 694.036, + "vacancy_rate": 0.000, + "square_feet_per_unit": 25.596, + "percent": 0.2737075, + "efficiency": 0.0396, + "household_size": 2.500, + "floor_area_ratio": 0.4023452351, + "building_use_definition": 3, + "gross_built_up_area": 17526.158, + "built_form": 403 + } + }, + { + "pk": 1297, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.2605425686, + "net_built_up_area": 693.298, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0127380, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0187246371, + "building_use_definition": 17, + "gross_built_up_area": 815.645, + "built_form": 403 + } + }, + { + "pk": 1298, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 36.1889300004, + "net_built_up_area": 11412.686, + "vacancy_rate": 0.000, + "square_feet_per_unit": 315.364, + "percent": 0.2166175, + "efficiency": 0.8228, + "household_size": 2.500, + "floor_area_ratio": 0.3184239342, + "building_use_definition": 15, + "gross_built_up_area": 13870.547, + "built_form": 403 + } + }, + { + "pk": 1299, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.9354590662, + "net_built_up_area": 1383.594, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0385920, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0373681705, + "building_use_definition": 21, + "gross_built_up_area": 1627.758, + "built_form": 404 + } + }, + { + "pk": 1300, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.5681026855, + "net_built_up_area": 2521.307, + "vacancy_rate": 0.000, + "square_feet_per_unit": 135.787, + "percent": 0.1647200, + "efficiency": 0.3629, + "household_size": 2.500, + "floor_area_ratio": 0.1594963994, + "building_use_definition": 5, + "gross_built_up_area": 6947.663, + "built_form": 404 + } + }, + { + "pk": 1301, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.2716878732, + "net_built_up_area": 1663.441, + "vacancy_rate": 0.000, + "square_feet_per_unit": 508.435, + "percent": 0.0450000, + "efficiency": 0.8764, + "household_size": 2.500, + "floor_area_ratio": 0.0435729600, + "building_use_definition": 7, + "gross_built_up_area": 1898.038, + "built_form": 404 + } + }, + { + "pk": 1302, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.0777647921, + "net_built_up_area": 4049.148, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1948.800, + "percent": 0.0960000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0929556480, + "building_use_definition": 12, + "gross_built_up_area": 4049.148, + "built_form": 404 + } + }, + { + "pk": 1303, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.7820888176, + "net_built_up_area": 3682.203, + "vacancy_rate": 0.000, + "square_feet_per_unit": 376.423, + "percent": 0.2435832, + "efficiency": 0.3584, + "household_size": 1.813, + "floor_area_ratio": 0.2358586896, + "building_use_definition": 2, + "gross_built_up_area": 10274.005, + "built_form": 404 + } + }, + { + "pk": 1304, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2753420658, + "net_built_up_area": 275.342, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0076800, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0074364518, + "building_use_definition": 10, + "gross_built_up_area": 323.932, + "built_form": 404 + } + }, + { + "pk": 1305, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6681414714, + "net_built_up_area": 3072.172, + "vacancy_rate": 0.000, + "square_feet_per_unit": 542.007, + "percent": 0.0850008, + "efficiency": 0.8569, + "household_size": 2.500, + "floor_area_ratio": 0.0823052546, + "building_use_definition": 9, + "gross_built_up_area": 3585.217, + "built_form": 404 + } + }, + { + "pk": 1306, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0240924308, + "net_built_up_area": 12.046, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0003360, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0003253448, + "building_use_definition": 13, + "gross_built_up_area": 14.172, + "built_form": 404 + } + }, + { + "pk": 1307, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.3770809912, + "net_built_up_area": 3682.276, + "vacancy_rate": 0.000, + "square_feet_per_unit": 841.263, + "percent": 0.1089912, + "efficiency": 0.8010, + "household_size": 1.595, + "floor_area_ratio": 0.1055348711, + "building_use_definition": 14, + "gross_built_up_area": 4597.099, + "built_form": 404 + } + }, + { + "pk": 1308, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.5455996783, + "net_built_up_area": 874.785, + "vacancy_rate": 0.000, + "square_feet_per_unit": 565.984, + "percent": 0.0244000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0236262272, + "building_use_definition": 16, + "gross_built_up_area": 1029.158, + "built_form": 404 + } + }, + { + "pk": 1309, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8894883423, + "net_built_up_area": 2509.628, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2821.429, + "percent": 0.0700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0677801600, + "building_use_definition": 8, + "gross_built_up_area": 2952.504, + "built_form": 404 + } + }, + { + "pk": 1310, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.8944414701, + "net_built_up_area": 490.754, + "vacancy_rate": 0.000, + "square_feet_per_unit": 49.599, + "percent": 0.1516968, + "efficiency": 0.0767, + "household_size": 2.500, + "floor_area_ratio": 0.1468861911, + "building_use_definition": 3, + "gross_built_up_area": 6398.362, + "built_form": 404 + } + }, + { + "pk": 1311, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8917328268, + "net_built_up_area": 490.453, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0136800, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0132461798, + "building_use_definition": 17, + "gross_built_up_area": 577.004, + "built_form": 404 + } + }, + { + "pk": 1312, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.9261952961, + "net_built_up_area": 2521.101, + "vacancy_rate": 0.000, + "square_feet_per_unit": 318.072, + "percent": 0.0703200, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0680900122, + "building_use_definition": 15, + "gross_built_up_area": 2966.001, + "built_form": 404 + } + }, + { + "pk": 1313, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8209651429, + "net_built_up_area": 3899.458, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2322.000, + "percent": 0.1128619, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0895192475, + "building_use_definition": 11, + "gross_built_up_area": 3899.458, + "built_form": 405 + } + }, + { + "pk": 1314, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.7573479082, + "net_built_up_area": 293.543, + "vacancy_rate": 0.000, + "square_feet_per_unit": 78.125, + "percent": 0.0800000, + "efficiency": 0.1062, + "household_size": 2.500, + "floor_area_ratio": 0.0634540071, + "building_use_definition": 5, + "gross_built_up_area": 2764.057, + "built_form": 405 + } + }, + { + "pk": 1315, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6213031151, + "net_built_up_area": 444.148, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0151235, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0119955834, + "building_use_definition": 21, + "gross_built_up_area": 522.528, + "built_form": 405 + } + }, + { + "pk": 1316, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.3218514167, + "net_built_up_area": 589.662, + "vacancy_rate": 0.000, + "square_feet_per_unit": 446.088, + "percent": 0.0174202, + "efficiency": 0.9797, + "household_size": 2.500, + "floor_area_ratio": 0.0138172687, + "building_use_definition": 7, + "gross_built_up_area": 601.880, + "built_form": 405 + } + }, + { + "pk": 1317, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.5011924720, + "net_built_up_area": 4874.324, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1948.800, + "percent": 0.1410774, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1118990792, + "building_use_definition": 12, + "gross_built_up_area": 4874.324, + "built_form": 405 + } + }, + { + "pk": 1318, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.0407951364, + "net_built_up_area": 3097.166, + "vacancy_rate": 0.000, + "square_feet_per_unit": 163.189, + "percent": 0.5214728, + "efficiency": 0.1719, + "household_size": 2.263, + "floor_area_ratio": 0.4136192341, + "building_use_definition": 2, + "gross_built_up_area": 18017.254, + "built_form": 405 + } + }, + { + "pk": 1319, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6121394567, + "net_built_up_area": 786.742, + "vacancy_rate": 0.000, + "square_feet_per_unit": 488.011, + "percent": 0.0254165, + "efficiency": 0.8959, + "household_size": 2.500, + "floor_area_ratio": 0.0201597346, + "building_use_definition": 9, + "gross_built_up_area": 878.158, + "built_form": 405 + } + }, + { + "pk": 1320, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8209651429, + "net_built_up_area": 4679.351, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2311.529, + "percent": 0.1354343, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1074231128, + "building_use_definition": 26, + "gross_built_up_area": 4679.351, + "built_form": 405 + } + }, + { + "pk": 1321, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.2583085222, + "net_built_up_area": 3097.881, + "vacancy_rate": 0.000, + "square_feet_per_unit": 727.491, + "percent": 0.1169757, + "efficiency": 0.7665, + "household_size": 1.672, + "floor_area_ratio": 0.0927822112, + "building_use_definition": 14, + "gross_built_up_area": 4041.593, + "built_form": 405 + } + }, + { + "pk": 1322, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4698896130, + "net_built_up_area": 293.681, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0079317509, + "building_use_definition": 16, + "gross_built_up_area": 345.507, + "built_form": 405 + } + }, + { + "pk": 1323, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7286261880, + "net_built_up_area": 2055.767, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2821.429, + "percent": 0.0700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0555222562, + "building_use_definition": 8, + "gross_built_up_area": 2418.549, + "built_form": 405 + } + }, + { + "pk": 1324, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.7172421759, + "net_built_up_area": 786.788, + "vacancy_rate": 0.000, + "square_feet_per_unit": 289.554, + "percent": 0.0428367, + "efficiency": 0.5316, + "household_size": 2.500, + "floor_area_ratio": 0.0339770033, + "building_use_definition": 3, + "gross_built_up_area": 1480.038, + "built_form": 405 + } + }, + { + "pk": 1325, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.7760322100, + "net_built_up_area": 2528.894, + "vacancy_rate": 0.000, + "square_feet_per_unit": 183.572, + "percent": 0.1468675, + "efficiency": 0.4275, + "household_size": 2.500, + "floor_area_ratio": 0.1358021354, + "building_use_definition": 5, + "gross_built_up_area": 5915.541, + "built_form": 406 + } + }, + { + "pk": 1326, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7059282063, + "net_built_up_area": 504.644, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0147400, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0136294516, + "building_use_definition": 21, + "gross_built_up_area": 593.699, + "built_form": 406 + } + }, + { + "pk": 1327, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.3839736449, + "net_built_up_area": 4932.320, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.611, + "percent": 0.1436105, + "efficiency": 0.8527, + "household_size": 2.500, + "floor_area_ratio": 0.1327905259, + "building_use_definition": 7, + "gross_built_up_area": 5784.355, + "built_form": 406 + } + }, + { + "pk": 1328, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.2133072413, + "net_built_up_area": 443.595, + "vacancy_rate": 0.000, + "square_feet_per_unit": 365.608, + "percent": 0.0292985, + "efficiency": 0.3759, + "household_size": 0.963, + "floor_area_ratio": 0.0270910778, + "building_use_definition": 2, + "gross_built_up_area": 1180.087, + "built_form": 406 + } + }, + { + "pk": 1329, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1692180209, + "net_built_up_area": 85.031, + "vacancy_rate": 0.000, + "square_feet_per_unit": 502.491, + "percent": 0.0441650, + "efficiency": 0.0478, + "household_size": 2.500, + "floor_area_ratio": 0.0408374985, + "building_use_definition": 10, + "gross_built_up_area": 1778.881, + "built_form": 406 + } + }, + { + "pk": 1330, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0094150014, + "net_built_up_area": 4.708, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0001375, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0001271404, + "building_use_definition": 13, + "gross_built_up_area": 5.538, + "built_form": 406 + } + }, + { + "pk": 1331, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6029063996, + "net_built_up_area": 443.603, + "vacancy_rate": 0.000, + "square_feet_per_unit": 735.774, + "percent": 0.0145585, + "efficiency": 0.7565, + "household_size": 1.208, + "floor_area_ratio": 0.0134616262, + "building_use_definition": 14, + "gross_built_up_area": 586.388, + "built_form": 406 + } + }, + { + "pk": 1332, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.4347940275, + "net_built_up_area": 2391.068, + "vacancy_rate": 0.000, + "square_feet_per_unit": 539.161, + "percent": 0.0698400, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0645780798, + "building_use_definition": 16, + "gross_built_up_area": 2813.021, + "built_form": 406 + } + }, + { + "pk": 1333, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.1785155589, + "net_built_up_area": 5974.223, + "vacancy_rate": 0.000, + "square_feet_per_unit": 534.438, + "percent": 0.1802460, + "efficiency": 0.8229, + "household_size": 2.500, + "floor_area_ratio": 0.1666658157, + "building_use_definition": 9, + "gross_built_up_area": 7259.963, + "built_form": 406 + } + }, + { + "pk": 1334, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 23.8843043458, + "net_built_up_area": 536.465, + "vacancy_rate": 0.000, + "square_feet_per_unit": 22.461, + "percent": 0.3838340, + "efficiency": 0.0347, + "household_size": 2.500, + "floor_area_ratio": 0.3549149869, + "building_use_definition": 3, + "gross_built_up_area": 15460.097, + "built_form": 406 + } + }, + { + "pk": 1335, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9757365050, + "net_built_up_area": 536.655, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0156750, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0144940063, + "building_use_definition": 17, + "gross_built_up_area": 631.359, + "built_form": 406 + } + }, + { + "pk": 1336, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.2259229368, + "net_built_up_area": 2529.174, + "vacancy_rate": 0.000, + "square_feet_per_unit": 350.014, + "percent": 0.0770275, + "efficiency": 0.8152, + "household_size": 2.500, + "floor_area_ratio": 0.0712240556, + "building_use_definition": 15, + "gross_built_up_area": 3102.520, + "built_form": 406 + } + }, + { + "pk": 1337, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.5061407391, + "net_built_up_area": 2577.874, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2174.650, + "percent": 0.0980400, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0591798329, + "building_use_definition": 26, + "gross_built_up_area": 2577.874, + "built_form": 407 + } + }, + { + "pk": 1338, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.3965757481, + "net_built_up_area": 486.547, + "vacancy_rate": 0.000, + "square_feet_per_unit": 65.780, + "percent": 0.1251964, + "efficiency": 0.1478, + "household_size": 2.500, + "floor_area_ratio": 0.0755722362, + "building_use_definition": 5, + "gross_built_up_area": 3291.927, + "built_form": 407 + } + }, + { + "pk": 1339, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.3143504018, + "net_built_up_area": 939.584, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0420396, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0253763413, + "building_use_definition": 21, + "gross_built_up_area": 1105.393, + "built_form": 407 + } + }, + { + "pk": 1340, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.4254553803, + "net_built_up_area": 1305.453, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.230, + "percent": 0.0579122, + "efficiency": 0.8573, + "household_size": 2.500, + "floor_area_ratio": 0.0349575104, + "building_use_definition": 7, + "gross_built_up_area": 1522.749, + "built_form": 407 + } + }, + { + "pk": 1341, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.3228004529, + "net_built_up_area": 2577.874, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1948.800, + "percent": 0.0980400, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0591798329, + "building_use_definition": 12, + "gross_built_up_area": 2577.874, + "built_form": 407 + } + }, + { + "pk": 1342, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.3612137085, + "net_built_up_area": 142.638, + "vacancy_rate": 0.000, + "square_feet_per_unit": 20.604, + "percent": 0.2454628, + "efficiency": 0.0221, + "household_size": 2.163, + "floor_area_ratio": 0.1481685791, + "building_use_definition": 2, + "gross_built_up_area": 6454.223, + "built_form": 407 + } + }, + { + "pk": 1343, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0423471031, + "net_built_up_area": 31.760, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0013726, + "efficiency": 0.8800, + "household_size": 2.500, + "floor_area_ratio": 0.0008285418, + "building_use_definition": 10, + "gross_built_up_area": 36.091, + "built_form": 407 + } + }, + { + "pk": 1344, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.5806710359, + "net_built_up_area": 2649.066, + "vacancy_rate": 0.000, + "square_feet_per_unit": 578.314, + "percent": 0.1169715, + "efficiency": 0.8613, + "household_size": 2.500, + "floor_area_ratio": 0.0706074442, + "building_use_definition": 9, + "gross_built_up_area": 3075.660, + "built_form": 407 + } + }, + { + "pk": 1345, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2074257372, + "net_built_up_area": 142.862, + "vacancy_rate": 0.000, + "square_feet_per_unit": 688.738, + "percent": 0.0073432, + "efficiency": 0.7399, + "household_size": 1.409, + "floor_area_ratio": 0.0044325719, + "building_use_definition": 14, + "gross_built_up_area": 193.083, + "built_form": 407 + } + }, + { + "pk": 1346, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.3490047781, + "net_built_up_area": 747.194, + "vacancy_rate": 0.000, + "square_feet_per_unit": 553.885, + "percent": 0.0334315, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0201802385, + "building_use_definition": 16, + "gross_built_up_area": 879.051, + "built_form": 407 + } + }, + { + "pk": 1347, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5545058638, + "net_built_up_area": 1564.499, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2821.429, + "percent": 0.0700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0422540627, + "building_use_definition": 8, + "gross_built_up_area": 1840.587, + "built_form": 407 + } + }, + { + "pk": 1348, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.1096473974, + "net_built_up_area": 520.477, + "vacancy_rate": 0.000, + "square_feet_per_unit": 64.180, + "percent": 0.1995408, + "efficiency": 0.0992, + "household_size": 2.500, + "floor_area_ratio": 0.1204487067, + "building_use_definition": 3, + "gross_built_up_area": 5246.746, + "built_form": 407 + } + }, + { + "pk": 1349, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9461967589, + "net_built_up_area": 520.408, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0232845, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0140552103, + "building_use_definition": 17, + "gross_built_up_area": 612.245, + "built_form": 407 + } + }, + { + "pk": 1350, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.2856064061, + "net_built_up_area": 486.445, + "vacancy_rate": 0.000, + "square_feet_per_unit": 378.378, + "percent": 0.0217649, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0131379350, + "building_use_definition": 15, + "gross_built_up_area": 572.288, + "built_form": 407 + } + }, + { + "pk": 1351, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4330059834, + "net_built_up_area": 7260.861, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2140.030, + "percent": 0.3306669, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1666864317, + "building_use_definition": 26, + "gross_built_up_area": 7260.861, + "built_form": 408 + } + }, + { + "pk": 1352, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.3879315309, + "net_built_up_area": 186.557, + "vacancy_rate": 0.000, + "square_feet_per_unit": 78.125, + "percent": 0.0800000, + "efficiency": 0.1062, + "household_size": 2.500, + "floor_area_ratio": 0.0403273340, + "building_use_definition": 5, + "gross_built_up_area": 1756.659, + "built_form": 408 + } + }, + { + "pk": 1353, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0758599514, + "net_built_up_area": 54.230, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0029055, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0014646384, + "building_use_definition": 21, + "gross_built_up_area": 63.800, + "built_form": 408 + } + }, + { + "pk": 1354, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.3209981140, + "net_built_up_area": 4523.161, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1948.800, + "percent": 0.2059893, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1038374914, + "building_use_definition": 12, + "gross_built_up_area": 4523.161, + "built_form": 408 + } + }, + { + "pk": 1355, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.7647079734, + "net_built_up_area": 7260.345, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1311.504, + "percent": 0.5395617, + "efficiency": 0.6128, + "household_size": 2.490, + "floor_area_ratio": 0.2719885614, + "building_use_definition": 2, + "gross_built_up_area": 11847.822, + "built_form": 408 + } + }, + { + "pk": 1356, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0896285608, + "net_built_up_area": 46.947, + "vacancy_rate": 0.000, + "square_feet_per_unit": 523.793, + "percent": 0.0025153, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0012679418, + "building_use_definition": 9, + "gross_built_up_area": 55.232, + "built_form": 408 + } + }, + { + "pk": 1357, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2986319741, + "net_built_up_area": 186.645, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0050409168, + "building_use_definition": 16, + "gross_built_up_area": 219.582, + "built_form": 408 + } + }, + { + "pk": 1358, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4630684970, + "net_built_up_area": 1306.515, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2821.429, + "percent": 0.0700000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0352864173, + "building_use_definition": 8, + "gross_built_up_area": 1537.076, + "built_form": 408 + } + }, + { + "pk": 1359, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0896285608, + "net_built_up_area": 46.947, + "vacancy_rate": 0.000, + "square_feet_per_unit": 523.793, + "percent": 0.0025153, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0012679418, + "building_use_definition": 3, + "gross_built_up_area": 55.232, + "built_form": 408 + } + }, + { + "pk": 1360, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.3934130184, + "net_built_up_area": 281.238, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0197880, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0075956785, + "building_use_definition": 21, + "gross_built_up_area": 330.868, + "built_form": 409 + } + }, + { + "pk": 1361, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6942477030, + "net_built_up_area": 360.232, + "vacancy_rate": 0.000, + "square_feet_per_unit": 133.704, + "percent": 0.0727106, + "efficiency": 0.2963, + "household_size": 2.500, + "floor_area_ratio": 0.0279101647, + "building_use_definition": 5, + "gross_built_up_area": 1215.767, + "built_form": 409 + } + }, + { + "pk": 1362, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.1892973793, + "net_built_up_area": 2080.918, + "vacancy_rate": 0.000, + "square_feet_per_unit": 652.469, + "percent": 0.1345865, + "efficiency": 0.9247, + "household_size": 2.500, + "floor_area_ratio": 0.0516613998, + "building_use_definition": 7, + "gross_built_up_area": 2250.371, + "built_form": 409 + } + }, + { + "pk": 1363, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.3934130184, + "net_built_up_area": 281.238, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0197880, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0075956785, + "building_use_definition": 2, + "gross_built_up_area": 330.868, + "built_form": 409 + } + }, + { + "pk": 1364, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2667707756, + "net_built_up_area": 200.078, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0127162, + "efficiency": 0.9410, + "household_size": 2.500, + "floor_area_ratio": 0.0048811485, + "building_use_definition": 10, + "gross_built_up_area": 212.623, + "built_form": 409 + } + }, + { + "pk": 1365, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.8409155399, + "net_built_up_area": 2505.994, + "vacancy_rate": 0.000, + "square_feet_per_unit": 652.447, + "percent": 0.1709139, + "efficiency": 0.8769, + "household_size": 2.500, + "floor_area_ratio": 0.0656057727, + "building_use_definition": 9, + "gross_built_up_area": 2857.787, + "built_form": 409 + } + }, + { + "pk": 1366, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1586582942, + "net_built_up_area": 657.369, + "vacancy_rate": 0.000, + "square_feet_per_unit": 567.354, + "percent": 0.0462528, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0177542651, + "building_use_definition": 16, + "gross_built_up_area": 773.376, + "built_form": 409 + } + }, + { + "pk": 1367, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0366658255, + "net_built_up_area": 18.333, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0011075, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0004251169, + "building_use_definition": 8, + "gross_built_up_area": 18.518, + "built_form": 409 + } + }, + { + "pk": 1368, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.7187912456, + "net_built_up_area": 276.682, + "vacancy_rate": 0.000, + "square_feet_per_unit": 31.734, + "percent": 0.3377011, + "efficiency": 0.0490, + "household_size": 2.500, + "floor_area_ratio": 0.1296275001, + "building_use_definition": 3, + "gross_built_up_area": 5646.574, + "built_form": 409 + } + }, + { + "pk": 1369, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.5034983404, + "net_built_up_area": 276.924, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0194845, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0074791792, + "building_use_definition": 17, + "gross_built_up_area": 325.793, + "built_form": 409 + } + }, + { + "pk": 1370, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9394958466, + "net_built_up_area": 360.292, + "vacancy_rate": 0.000, + "square_feet_per_unit": 383.495, + "percent": 0.0253503, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0097307827, + "building_use_definition": 15, + "gross_built_up_area": 423.873, + "built_form": 409 + } + }, + { + "pk": 1371, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7600450793, + "net_built_up_area": 3792.682, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2322.000, + "percent": 0.2311965, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0870679928, + "building_use_definition": 11, + "gross_built_up_area": 3792.682, + "built_form": 410 + } + }, + { + "pk": 1372, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.3389245127, + "net_built_up_area": 139.472, + "vacancy_rate": 0.000, + "square_feet_per_unit": 104.167, + "percent": 0.0600000, + "efficiency": 0.1417, + "household_size": 2.500, + "floor_area_ratio": 0.0225958419, + "building_use_definition": 5, + "gross_built_up_area": 984.275, + "built_form": 410 + } + }, + { + "pk": 1373, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0604283653, + "net_built_up_area": 43.198, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0030980, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0011666986, + "building_use_definition": 21, + "gross_built_up_area": 50.821, + "built_form": 410 + } + }, + { + "pk": 1374, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7562776926, + "net_built_up_area": 5594.673, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1314.956, + "percent": 0.5753093, + "efficiency": 0.5928, + "household_size": 2.490, + "floor_area_ratio": 0.2166599667, + "building_use_definition": 2, + "gross_built_up_area": 9437.708, + "built_form": 410 + } + }, + { + "pk": 1375, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0713948630, + "net_built_up_area": 37.396, + "vacancy_rate": 0.000, + "square_feet_per_unit": 523.793, + "percent": 0.0026819, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0010099965, + "building_use_definition": 9, + "gross_built_up_area": 43.995, + "built_form": 410 + } + }, + { + "pk": 1376, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7600450793, + "net_built_up_area": 5594.205, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2218.398, + "percent": 0.3410148, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1284252753, + "building_use_definition": 26, + "gross_built_up_area": 5594.205, + "built_form": 410 + } + }, + { + "pk": 1377, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2231023049, + "net_built_up_area": 139.439, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0037659737, + "building_use_definition": 16, + "gross_built_up_area": 164.046, + "built_form": 410 + } + }, + { + "pk": 1378, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2363371874, + "net_built_up_area": 697.195, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2950.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0188298683, + "building_use_definition": 8, + "gross_built_up_area": 820.229, + "built_form": 410 + } + }, + { + "pk": 1379, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0713948630, + "net_built_up_area": 37.396, + "vacancy_rate": 0.000, + "square_feet_per_unit": 523.793, + "percent": 0.0026819, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0010099965, + "building_use_definition": 3, + "gross_built_up_area": 43.995, + "built_form": 410 + } + }, + { + "pk": 1380, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0560464827, + "net_built_up_area": 4624.635, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3430.534, + "percent": 0.5086323, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1061670108, + "building_use_definition": 11, + "gross_built_up_area": 4624.635, + "built_form": 411 + } + }, + { + "pk": 1381, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7421034813, + "net_built_up_area": 77.303, + "vacancy_rate": 0.000, + "square_feet_per_unit": 104.167, + "percent": 0.0600000, + "efficiency": 0.1417, + "household_size": 2.500, + "floor_area_ratio": 0.0125238225, + "building_use_definition": 5, + "gross_built_up_area": 545.538, + "built_form": 411 + } + }, + { + "pk": 1382, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0334926277, + "net_built_up_area": 23.943, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0030980, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0006466467, + "building_use_definition": 21, + "gross_built_up_area": 28.168, + "built_form": 411 + } + }, + { + "pk": 1383, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0559250318, + "net_built_up_area": 578.013, + "vacancy_rate": 0.000, + "square_feet_per_unit": 242.684, + "percent": 0.5753094, + "efficiency": 0.1105, + "household_size": 2.490, + "floor_area_ratio": 0.1200845469, + "building_use_definition": 2, + "gross_built_up_area": 5230.883, + "built_form": 411 + } + }, + { + "pk": 1384, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0395708465, + "net_built_up_area": 20.727, + "vacancy_rate": 0.000, + "square_feet_per_unit": 523.793, + "percent": 0.0026819, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0005597940, + "building_use_definition": 9, + "gross_built_up_area": 24.385, + "built_form": 411 + } + }, + { + "pk": 1385, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0560464827, + "net_built_up_area": 578.080, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2195.982, + "percent": 0.0635791, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0132708894, + "building_use_definition": 26, + "gross_built_up_area": 578.080, + "built_form": 411 + } + }, + { + "pk": 1386, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1236552140, + "net_built_up_area": 77.285, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 0.0100000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0020873038, + "building_use_definition": 16, + "gross_built_up_area": 90.923, + "built_form": 411 + } + }, + { + "pk": 1387, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1309906928, + "net_built_up_area": 386.423, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2950.000, + "percent": 0.0500000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0104365188, + "building_use_definition": 8, + "gross_built_up_area": 454.615, + "built_form": 411 + } + }, + { + "pk": 1388, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0395708465, + "net_built_up_area": 20.727, + "vacancy_rate": 0.000, + "square_feet_per_unit": 523.793, + "percent": 0.0026819, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0005597940, + "building_use_definition": 3, + "gross_built_up_area": 24.385, + "built_form": 411 + } + }, + { + "pk": 1389, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1829329254, + "net_built_up_area": 182.933, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0071379, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0049406613, + "building_use_definition": 18, + "gross_built_up_area": 215.215, + "built_form": 412 + } + }, + { + "pk": 1390, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.0535942165, + "net_built_up_area": 182.912, + "vacancy_rate": 0.000, + "square_feet_per_unit": 89.069, + "percent": 0.0801388, + "efficiency": 0.0757, + "household_size": 2.500, + "floor_area_ratio": 0.0554699096, + "building_use_definition": 4, + "gross_built_up_area": 2416.269, + "built_form": 412 + } + }, + { + "pk": 1391, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 44.9118132487, + "net_built_up_area": 13403.571, + "vacancy_rate": 0.000, + "square_feet_per_unit": 298.442, + "percent": 0.5742764, + "efficiency": 0.7741, + "household_size": 2.500, + "floor_area_ratio": 0.3974985901, + "building_use_definition": 5, + "gross_built_up_area": 17315.039, + "built_form": 412 + } + }, + { + "pk": 1392, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1308203173, + "net_built_up_area": 98.115, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0035689, + "efficiency": 0.9118, + "household_size": 2.500, + "floor_area_ratio": 0.0024702960, + "building_use_definition": 7, + "gross_built_up_area": 107.606, + "built_form": 412 + } + }, + { + "pk": 1393, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0831513297, + "net_built_up_area": 99.782, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 0.0038934, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0026949062, + "building_use_definition": 19, + "gross_built_up_area": 117.390, + "built_form": 412 + } + }, + { + "pk": 1394, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.0729448485, + "net_built_up_area": 1771.114, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1650.704, + "percent": 0.0691075, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0478343422, + "building_use_definition": 20, + "gross_built_up_area": 2083.664, + "built_form": 412 + } + }, + { + "pk": 1395, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0459125774, + "net_built_up_area": 34.434, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0012978, + "efficiency": 0.8800, + "household_size": 2.500, + "floor_area_ratio": 0.0008983021, + "building_use_definition": 10, + "gross_built_up_area": 39.130, + "built_form": 412 + } + }, + { + "pk": 1396, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2217368793, + "net_built_up_area": 166.303, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0064890, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0044915103, + "building_use_definition": 13, + "gross_built_up_area": 195.650, + "built_form": 412 + } + }, + { + "pk": 1397, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.1141845866, + "net_built_up_area": 1314.680, + "vacancy_rate": 0.000, + "square_feet_per_unit": 621.838, + "percent": 0.0512978, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0355069496, + "building_use_definition": 16, + "gross_built_up_area": 1546.683, + "built_form": 412 + } + }, + { + "pk": 1398, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.2227496512, + "net_built_up_area": 883.829, + "vacancy_rate": 0.000, + "square_feet_per_unit": 722.821, + "percent": 0.0331262, + "efficiency": 0.8849, + "household_size": 2.500, + "floor_area_ratio": 0.0229290596, + "building_use_definition": 9, + "gross_built_up_area": 998.790, + "built_form": 412 + } + }, + { + "pk": 1399, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6419219405, + "net_built_up_area": 883.835, + "vacancy_rate": 0.000, + "square_feet_per_unit": 538.293, + "percent": 0.0444819, + "efficiency": 0.6590, + "household_size": 2.500, + "floor_area_ratio": 0.0307891680, + "building_use_definition": 3, + "gross_built_up_area": 1341.176, + "built_form": 412 + } + }, + { + "pk": 1400, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 40.8985294685, + "net_built_up_area": 13403.102, + "vacancy_rate": 0.000, + "square_feet_per_unit": 327.716, + "percent": 0.5229786, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3619916406, + "building_use_definition": 15, + "gross_built_up_area": 15768.356, + "built_form": 412 + } + }, + { + "pk": 1401, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1780039638, + "net_built_up_area": 1178.004, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0637045, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0318155881, + "building_use_definition": 18, + "gross_built_up_area": 1385.887, + "built_form": 413 + } + }, + { + "pk": 1402, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.1603662113, + "net_built_up_area": 1178.062, + "vacancy_rate": 0.000, + "square_feet_per_unit": 283.163, + "percent": 0.2249750, + "efficiency": 0.2407, + "household_size": 2.500, + "floor_area_ratio": 0.1123580269, + "building_use_definition": 4, + "gross_built_up_area": 4894.316, + "built_form": 413 + } + }, + { + "pk": 1403, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.7591153123, + "net_built_up_area": 7295.604, + "vacancy_rate": 0.000, + "square_feet_per_unit": 410.809, + "percent": 0.3956980, + "efficiency": 0.8475, + "household_size": 2.500, + "floor_area_ratio": 0.1976212758, + "building_use_definition": 5, + "gross_built_up_area": 8608.383, + "built_form": 413 + } + }, + { + "pk": 1404, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.3161167930, + "net_built_up_area": 212.063, + "vacancy_rate": 0.000, + "square_feet_per_unit": 670.836, + "percent": 0.0107001, + "efficiency": 0.9110, + "household_size": 2.500, + "floor_area_ratio": 0.0053438921, + "building_use_definition": 7, + "gross_built_up_area": 232.780, + "built_form": 413 + } + }, + { + "pk": 1405, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.3015548008, + "net_built_up_area": 361.866, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 0.0195691, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0097732880, + "building_use_definition": 19, + "gross_built_up_area": 425.724, + "built_form": 413 + } + }, + { + "pk": 1406, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.0097225120, + "net_built_up_area": 2620.299, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2595.068, + "percent": 0.1417014, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0707691509, + "building_use_definition": 20, + "gross_built_up_area": 3082.704, + "built_form": 413 + } + }, + { + "pk": 1407, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0429859318, + "net_built_up_area": 32.239, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0016424, + "efficiency": 0.9023, + "household_size": 2.500, + "floor_area_ratio": 0.0008202548, + "building_use_definition": 10, + "gross_built_up_area": 35.730, + "built_form": 413 + } + }, + { + "pk": 1408, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6940374928, + "net_built_up_area": 533.103, + "vacancy_rate": 0.000, + "square_feet_per_unit": 768.118, + "percent": 0.0274842, + "efficiency": 0.8916, + "household_size": 2.500, + "floor_area_ratio": 0.0137262828, + "building_use_definition": 9, + "gross_built_up_area": 597.917, + "built_form": 413 + } + }, + { + "pk": 1409, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1895497005, + "net_built_up_area": 142.162, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0076879, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0038395256, + "building_use_definition": 13, + "gross_built_up_area": 167.250, + "built_form": 413 + } + }, + { + "pk": 1410, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0413548190, + "net_built_up_area": 20.677, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0011182, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0005584565, + "building_use_definition": 16, + "gross_built_up_area": 24.326, + "built_form": 413 + } + }, + { + "pk": 1411, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0022571176, + "net_built_up_area": 1.129, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0000524, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0000261698, + "building_use_definition": 8, + "gross_built_up_area": 1.140, + "built_form": 413 + } + }, + { + "pk": 1412, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.6300234059, + "net_built_up_area": 567.877, + "vacancy_rate": 0.000, + "square_feet_per_unit": 215.921, + "percent": 0.0782241, + "efficiency": 0.3337, + "household_size": 2.500, + "floor_area_ratio": 0.0390670320, + "building_use_definition": 3, + "gross_built_up_area": 1701.760, + "built_form": 413 + } + }, + { + "pk": 1413, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.0324920025, + "net_built_up_area": 567.871, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0307095, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0153370767, + "building_use_definition": 17, + "gross_built_up_area": 668.083, + "built_form": 413 + } + }, + { + "pk": 1414, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 17.7062699262, + "net_built_up_area": 7295.479, + "vacancy_rate": 0.000, + "square_feet_per_unit": 412.028, + "percent": 0.3945274, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1970366495, + "building_use_definition": 15, + "gross_built_up_area": 8582.916, + "built_form": 413 + } + }, + { + "pk": 1415, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.5545111376, + "net_built_up_area": 1935.228, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1244.911, + "percent": 0.1290195, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0522667319, + "building_use_definition": 18, + "gross_built_up_area": 2276.739, + "built_form": 414 + } + }, + { + "pk": 1416, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6904907450, + "net_built_up_area": 2296.784, + "vacancy_rate": 0.000, + "square_feet_per_unit": 403.618, + "percent": 0.1544322, + "efficiency": 0.8428, + "household_size": 2.500, + "floor_area_ratio": 0.0625616003, + "building_use_definition": 5, + "gross_built_up_area": 2725.183, + "built_form": 414 + } + }, + { + "pk": 1417, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6516966737, + "net_built_up_area": 488.773, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0282835, + "efficiency": 0.9793, + "household_size": 2.500, + "floor_area_ratio": 0.0114578503, + "building_use_definition": 7, + "gross_built_up_area": 499.104, + "built_form": 414 + } + }, + { + "pk": 1418, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8751044873, + "net_built_up_area": 1007.552, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1151.351, + "percent": 0.0671724, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0272120247, + "building_use_definition": 19, + "gross_built_up_area": 1185.356, + "built_form": 414 + } + }, + { + "pk": 1419, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.5578189726, + "net_built_up_area": 4836.595, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3104.722, + "percent": 0.3224504, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1306269874, + "building_use_definition": 20, + "gross_built_up_area": 5690.112, + "built_form": 414 + } + }, + { + "pk": 1420, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1075662204, + "net_built_up_area": 80.675, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0048791, + "efficiency": 0.9370, + "household_size": 2.500, + "floor_area_ratio": 0.0019765587, + "building_use_definition": 10, + "gross_built_up_area": 86.099, + "built_form": 414 + } + }, + { + "pk": 1421, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.8835284860, + "net_built_up_area": 673.812, + "vacancy_rate": 0.000, + "square_feet_per_unit": 762.638, + "percent": 0.0426446, + "efficiency": 0.8954, + "household_size": 2.500, + "floor_area_ratio": 0.0172756357, + "building_use_definition": 9, + "gross_built_up_area": 752.527, + "built_form": 414 + } + }, + { + "pk": 1422, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1512849589, + "net_built_up_area": 113.464, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0075645, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0030644336, + "building_use_definition": 13, + "gross_built_up_area": 133.487, + "built_form": 414 + } + }, + { + "pk": 1423, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.2474818046, + "net_built_up_area": 1934.776, + "vacancy_rate": 0.000, + "square_feet_per_unit": 309.689, + "percent": 0.5186423, + "efficiency": 0.2114, + "household_size": 2.500, + "floor_area_ratio": 0.2101057440, + "building_use_definition": 4, + "gross_built_up_area": 9152.206, + "built_form": 414 + } + }, + { + "pk": 1424, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0272300926, + "net_built_up_area": 13.615, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0009077, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0003677158, + "building_use_definition": 16, + "gross_built_up_area": 16.018, + "built_form": 414 + } + }, + { + "pk": 1425, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0138746788, + "net_built_up_area": 6.937, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0003971, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0001608681, + "building_use_definition": 8, + "gross_built_up_area": 7.007, + "built_form": 414 + } + }, + { + "pk": 1426, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.7273418424, + "net_built_up_area": 673.817, + "vacancy_rate": 0.000, + "square_feet_per_unit": 390.089, + "percent": 0.0833717, + "efficiency": 0.4580, + "household_size": 2.500, + "floor_area_ratio": 0.0337744782, + "building_use_definition": 3, + "gross_built_up_area": 1471.216, + "built_form": 414 + } + }, + { + "pk": 1427, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.6425376925, + "net_built_up_area": 2296.834, + "vacancy_rate": 0.000, + "square_feet_per_unit": 407.057, + "percent": 0.1531274, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0620330164, + "building_use_definition": 15, + "gross_built_up_area": 2702.158, + "built_form": 414 + } + }, + { + "pk": 1428, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.0911011970, + "net_built_up_area": 2649.969, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1267.260, + "percent": 0.2246282, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0715704884, + "building_use_definition": 18, + "gross_built_up_area": 3117.610, + "built_form": 415 + } + }, + { + "pk": 1429, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1751159131, + "net_built_up_area": 465.089, + "vacancy_rate": 0.000, + "square_feet_per_unit": 395.781, + "percent": 0.0405399, + "efficiency": 0.8266, + "household_size": 2.500, + "floor_area_ratio": 0.0129167239, + "building_use_definition": 5, + "gross_built_up_area": 562.652, + "built_form": 415 + } + }, + { + "pk": 1430, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.3230033667, + "net_built_up_area": 992.253, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0725083, + "efficiency": 0.9860, + "household_size": 2.500, + "floor_area_ratio": 0.0231024174, + "building_use_definition": 7, + "gross_built_up_area": 1006.341, + "built_form": 415 + } + }, + { + "pk": 1431, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7235440497, + "net_built_up_area": 759.721, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1050.000, + "percent": 0.0643988, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0205185883, + "building_use_definition": 19, + "gross_built_up_area": 893.790, + "built_form": 415 + } + }, + { + "pk": 1432, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1324900286, + "net_built_up_area": 3100.085, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2737.406, + "percent": 0.2627829, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0837272457, + "building_use_definition": 20, + "gross_built_up_area": 3647.159, + "built_form": 415 + } + }, + { + "pk": 1433, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1965501474, + "net_built_up_area": 147.413, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0111803, + "efficiency": 0.9500, + "household_size": 2.500, + "floor_area_ratio": 0.0035622399, + "building_use_definition": 10, + "gross_built_up_area": 155.171, + "built_form": 415 + } + }, + { + "pk": 1434, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.0904680747, + "net_built_up_area": 817.851, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0655914, + "efficiency": 0.8984, + "household_size": 2.500, + "floor_area_ratio": 0.0208985716, + "building_use_definition": 9, + "gross_built_up_area": 910.342, + "built_form": 415 + } + }, + { + "pk": 1435, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0586207437, + "net_built_up_area": 43.966, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0037268, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0011874239, + "building_use_definition": 13, + "gross_built_up_area": 51.724, + "built_form": 415 + } + }, + { + "pk": 1436, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.1366844666, + "net_built_up_area": 2649.861, + "vacancy_rate": 0.000, + "square_feet_per_unit": 515.870, + "percent": 0.5518099, + "efficiency": 0.3460, + "household_size": 2.500, + "floor_area_ratio": 0.1758163224, + "building_use_definition": 4, + "gross_built_up_area": 7658.559, + "built_form": 415 + } + }, + { + "pk": 1437, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0307230670, + "net_built_up_area": 15.362, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0011180, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0003562144, + "building_use_definition": 8, + "gross_built_up_area": 15.517, + "built_form": 415 + } + }, + { + "pk": 1438, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.5435755331, + "net_built_up_area": 817.790, + "vacancy_rate": 0.000, + "square_feet_per_unit": 321.512, + "percent": 0.1530068, + "efficiency": 0.3851, + "household_size": 2.500, + "floor_area_ratio": 0.0487506529, + "building_use_definition": 3, + "gross_built_up_area": 2123.578, + "built_form": 415 + } + }, + { + "pk": 1439, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1426528521, + "net_built_up_area": 465.065, + "vacancy_rate": 0.000, + "square_feet_per_unit": 407.005, + "percent": 0.0394219, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0125605095, + "building_use_definition": 15, + "gross_built_up_area": 547.136, + "built_form": 415 + } + }, + { + "pk": 1440, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.4828931226, + "net_built_up_area": 1516.103, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1022.395, + "percent": 0.3871952, + "efficiency": 0.6410, + "household_size": 2.500, + "floor_area_ratio": 0.0542978542, + "building_use_definition": 4, + "gross_built_up_area": 2365.215, + "built_form": 416 + } + }, + { + "pk": 1441, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4216144903, + "net_built_up_area": 168.646, + "vacancy_rate": 0.000, + "square_feet_per_unit": 400.000, + "percent": 0.0324800, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0045547938, + "building_use_definition": 5, + "gross_built_up_area": 198.407, + "built_form": 416 + } + }, + { + "pk": 1442, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0022486106, + "net_built_up_area": 1.686, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0003248, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000455479, + "building_use_definition": 7, + "gross_built_up_area": 1.984, + "built_form": 416 + } + }, + { + "pk": 1443, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 48.1198356237, + "net_built_up_area": 3.998, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0007700, + "efficiency": 0.8500, + "household_size": 0.000, + "floor_area_ratio": 0.0001079800, + "building_use_definition": 2, + "gross_built_up_area": 4.704, + "built_form": 416 + } + }, + { + "pk": 1444, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1183103018, + "net_built_up_area": 1516.126, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1355.729, + "percent": 0.2919952, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0409475965, + "building_use_definition": 18, + "gross_built_up_area": 1783.677, + "built_form": 416 + } + }, + { + "pk": 1445, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1017222675, + "net_built_up_area": 395.445, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3887.500, + "percent": 0.0761600, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0106802062, + "building_use_definition": 20, + "gross_built_up_area": 465.230, + "built_form": 416 + } + }, + { + "pk": 1446, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0042760090, + "net_built_up_area": 42.760, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.0070000, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0009816366, + "building_use_definition": 23, + "gross_built_up_area": 42.760, + "built_form": 416 + } + }, + { + "pk": 1447, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 48.1198356237, + "net_built_up_area": 3.998, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0007700, + "efficiency": 0.8500, + "household_size": 0.000, + "floor_area_ratio": 0.0001079800, + "building_use_definition": 11, + "gross_built_up_area": 4.704, + "built_form": 416 + } + }, + { + "pk": 1448, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0823844406, + "net_built_up_area": 98.861, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 0.0190400, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0026700516, + "building_use_definition": 19, + "gross_built_up_area": 116.307, + "built_form": 416 + } + }, + { + "pk": 1449, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0850477151, + "net_built_up_area": 807.718, + "vacancy_rate": 0.000, + "square_feet_per_unit": 9497.235, + "percent": 0.1392300, + "efficiency": 0.9497, + "household_size": 2.500, + "floor_area_ratio": 0.0195247520, + "building_use_definition": 6, + "gross_built_up_area": 850.498, + "built_form": 416 + } + }, + { + "pk": 1450, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0022486106, + "net_built_up_area": 1.686, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0003248, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000455479, + "building_use_definition": 3, + "gross_built_up_area": 1.984, + "built_form": 416 + } + }, + { + "pk": 1451, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0807738106, + "net_built_up_area": 807.738, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.1322300, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0185431154, + "building_use_definition": 22, + "gross_built_up_area": 807.738, + "built_form": 416 + } + }, + { + "pk": 1452, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4216144903, + "net_built_up_area": 168.646, + "vacancy_rate": 0.000, + "square_feet_per_unit": 400.000, + "percent": 0.0324800, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0045547938, + "building_use_definition": 15, + "gross_built_up_area": 198.407, + "built_form": 416 + } + }, + { + "pk": 1453, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.0991255801, + "net_built_up_area": 6377.123, + "vacancy_rate": 0.000, + "square_feet_per_unit": 288.569, + "percent": 0.1283333, + "efficiency": 0.7273, + "household_size": 2.500, + "floor_area_ratio": 0.2012905125, + "building_use_definition": 5, + "gross_built_up_area": 8768.215, + "built_form": 417 + } + }, + { + "pk": 1454, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.7887567922, + "net_built_up_area": 11829.748, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1742.550, + "percent": 0.1731425, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.2715736489, + "building_use_definition": 12, + "gross_built_up_area": 11829.748, + "built_form": 417 + } + }, + { + "pk": 1455, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.0889240812, + "net_built_up_area": 3594.113, + "vacancy_rate": 0.000, + "square_feet_per_unit": 507.004, + "percent": 0.0595541, + "efficiency": 0.8833, + "household_size": 2.500, + "floor_area_ratio": 0.0934104812, + "building_use_definition": 7, + "gross_built_up_area": 4068.961, + "built_form": 417 + } + }, + { + "pk": 1456, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 26.0062271872, + "net_built_up_area": 12123.271, + "vacancy_rate": 0.000, + "square_feet_per_unit": 466.168, + "percent": 0.3923028, + "efficiency": 0.4523, + "household_size": 2.166, + "floor_area_ratio": 0.6153261209, + "building_use_definition": 2, + "gross_built_up_area": 26803.606, + "built_form": 417 + } + }, + { + "pk": 1457, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2493093932, + "net_built_up_area": 124.655, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0204308, + "efficiency": 0.0893, + "household_size": 2.500, + "floor_area_ratio": 0.0320456670, + "building_use_definition": 10, + "gross_built_up_area": 1395.909, + "built_form": 417 + } + }, + { + "pk": 1458, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0643589405, + "net_built_up_area": 32.179, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0005541, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0008691047, + "building_use_definition": 13, + "gross_built_up_area": 37.858, + "built_form": 417 + } + }, + { + "pk": 1459, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 14.5278651387, + "net_built_up_area": 12122.835, + "vacancy_rate": 0.000, + "square_feet_per_unit": 834.454, + "percent": 0.2191603, + "efficiency": 0.8096, + "household_size": 1.903, + "floor_area_ratio": 0.3437524720, + "building_use_definition": 14, + "gross_built_up_area": 14973.858, + "built_form": 417 + } + }, + { + "pk": 1460, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6088490667, + "net_built_up_area": 844.646, + "vacancy_rate": 0.000, + "square_feet_per_unit": 525.000, + "percent": 0.0145440, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0228122336, + "building_use_definition": 16, + "gross_built_up_area": 993.701, + "built_form": 417 + } + }, + { + "pk": 1461, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 6.6227071133, + "net_built_up_area": 3354.540, + "vacancy_rate": 0.000, + "square_feet_per_unit": 506.521, + "percent": 0.0646618, + "efficiency": 0.7593, + "household_size": 2.500, + "floor_area_ratio": 0.1014218980, + "building_use_definition": 9, + "gross_built_up_area": 4417.938, + "built_form": 417 + } + }, + { + "pk": 1462, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.1598635018, + "net_built_up_area": 1552.977, + "vacancy_rate": 0.000, + "square_feet_per_unit": 85.517, + "percent": 0.1719340, + "efficiency": 0.1322, + "household_size": 2.500, + "floor_area_ratio": 0.2696781192, + "building_use_definition": 3, + "gross_built_up_area": 11747.179, + "built_form": 417 + } + }, + { + "pk": 1463, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.8227927833, + "net_built_up_area": 1552.536, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0267332, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0419309683, + "building_use_definition": 17, + "gross_built_up_area": 1826.513, + "built_form": 417 + } + }, + { + "pk": 1464, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 19.5955597136, + "net_built_up_area": 6377.434, + "vacancy_rate": 0.000, + "square_feet_per_unit": 325.453, + "percent": 0.1137893, + "efficiency": 0.8203, + "household_size": 2.500, + "floor_area_ratio": 0.1784782789, + "building_use_definition": 15, + "gross_built_up_area": 7774.514, + "built_form": 417 + } + }, + { + "pk": 1465, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4327710665, + "net_built_up_area": 309.373, + "vacancy_rate": 0.000, + "square_feet_per_unit": 714.866, + "percent": 0.0185609, + "efficiency": 0.8500, + "household_size": 0.721, + "floor_area_ratio": 0.0083555696, + "building_use_definition": 21, + "gross_built_up_area": 363.969, + "built_form": 418 + } + }, + { + "pk": 1466, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.2933960580, + "net_built_up_area": 510.801, + "vacancy_rate": 0.000, + "square_feet_per_unit": 222.727, + "percent": 0.0571370, + "efficiency": 0.4559, + "household_size": 2.500, + "floor_area_ratio": 0.0257213919, + "building_use_definition": 5, + "gross_built_up_area": 1120.424, + "built_form": 418 + } + }, + { + "pk": 1467, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.8459576951, + "net_built_up_area": 3336.747, + "vacancy_rate": 0.000, + "square_feet_per_unit": 688.563, + "percent": 0.1831848, + "efficiency": 0.9289, + "household_size": 2.500, + "floor_area_ratio": 0.0824643930, + "building_use_definition": 7, + "gross_built_up_area": 3592.149, + "built_form": 418 + } + }, + { + "pk": 1468, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4050614663, + "net_built_up_area": 679.045, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1676.400, + "percent": 0.0346285, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0155887292, + "building_use_definition": 12, + "gross_built_up_area": 679.045, + "built_form": 418 + } + }, + { + "pk": 1469, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6444244012, + "net_built_up_area": 577.234, + "vacancy_rate": 0.000, + "square_feet_per_unit": 351.025, + "percent": 0.0878179, + "efficiency": 0.3352, + "household_size": 2.124, + "floor_area_ratio": 0.0395330280, + "building_use_definition": 2, + "gross_built_up_area": 1722.059, + "built_form": 418 + } + }, + { + "pk": 1470, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9617807047, + "net_built_up_area": 721.336, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0412945, + "efficiency": 0.8908, + "household_size": 2.500, + "floor_area_ratio": 0.0185895657, + "building_use_definition": 10, + "gross_built_up_area": 809.761, + "built_form": 418 + } + }, + { + "pk": 1471, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.0638762129, + "net_built_up_area": 4997.064, + "vacancy_rate": 0.000, + "square_feet_per_unit": 707.411, + "percent": 0.2902388, + "efficiency": 0.8780, + "household_size": 2.500, + "floor_area_ratio": 0.1306569457, + "building_use_definition": 9, + "gross_built_up_area": 5691.417, + "built_form": 418 + } + }, + { + "pk": 1472, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6483804604, + "net_built_up_area": 577.188, + "vacancy_rate": 0.000, + "square_feet_per_unit": 890.200, + "percent": 0.0346285, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0155887292, + "building_use_definition": 14, + "gross_built_up_area": 679.045, + "built_form": 418 + } + }, + { + "pk": 1473, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7979049570, + "net_built_up_area": 415.575, + "vacancy_rate": 0.000, + "square_feet_per_unit": 520.833, + "percent": 0.0249325, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0112238760, + "building_use_definition": 16, + "gross_built_up_area": 488.912, + "built_form": 418 + } + }, + { + "pk": 1474, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0605035927, + "net_built_up_area": 30.252, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0015583, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0007015007, + "building_use_definition": 8, + "gross_built_up_area": 30.557, + "built_form": 418 + } + }, + { + "pk": 1475, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 16.6081500055, + "net_built_up_area": 548.733, + "vacancy_rate": 0.000, + "square_feet_per_unit": 33.040, + "percent": 0.5476152, + "efficiency": 0.0511, + "household_size": 2.500, + "floor_area_ratio": 0.2465202084, + "building_use_definition": 3, + "gross_built_up_area": 10738.420, + "built_form": 418 + } + }, + { + "pk": 1476, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9969623423, + "net_built_up_area": 548.329, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0328971, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0148093040, + "building_use_definition": 17, + "gross_built_up_area": 645.093, + "built_form": 418 + } + }, + { + "pk": 1477, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.2301176098, + "net_built_up_area": 510.811, + "vacancy_rate": 0.000, + "square_feet_per_unit": 415.254, + "percent": 0.0306462, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0137960152, + "building_use_definition": 15, + "gross_built_up_area": 600.954, + "built_form": 418 + } + }, + { + "pk": 1478, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2285625295, + "net_built_up_area": 3221.025, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3380.571, + "percent": 0.2612234, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0739445615, + "building_use_definition": 11, + "gross_built_up_area": 3221.025, + "built_form": 419 + } + }, + { + "pk": 1479, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1409502208, + "net_built_up_area": 230.073, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1632.300, + "percent": 0.0186588, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0052817504, + "building_use_definition": 12, + "gross_built_up_area": 230.073, + "built_form": 419 + } + }, + { + "pk": 1480, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.7350632692, + "net_built_up_area": 73.026, + "vacancy_rate": 0.000, + "square_feet_per_unit": 26.700, + "percent": 0.1304490, + "efficiency": 0.0454, + "household_size": 2.500, + "floor_area_ratio": 0.0369262253, + "building_use_definition": 5, + "gross_built_up_area": 1608.506, + "built_form": 419 + } + }, + { + "pk": 1481, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6588384927, + "net_built_up_area": 1242.118, + "vacancy_rate": 0.000, + "square_feet_per_unit": 748.788, + "percent": 0.1026234, + "efficiency": 0.9816, + "household_size": 2.500, + "floor_area_ratio": 0.0290496269, + "building_use_definition": 7, + "gross_built_up_area": 1265.402, + "built_form": 419 + } + }, + { + "pk": 1482, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2211995871, + "net_built_up_area": 130.329, + "vacancy_rate": 0.000, + "square_feet_per_unit": 30.697, + "percent": 0.3607370, + "efficiency": 0.0293, + "household_size": 2.500, + "floor_area_ratio": 0.1021138968, + "building_use_definition": 2, + "gross_built_up_area": 4448.081, + "built_form": 419 + } + }, + { + "pk": 1483, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2619452364, + "net_built_up_area": 196.459, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0169173, + "efficiency": 0.9418, + "household_size": 2.500, + "floor_area_ratio": 0.0047887836, + "building_use_definition": 10, + "gross_built_up_area": 208.599, + "built_form": 419 + } + }, + { + "pk": 1484, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.7385749109, + "net_built_up_area": 1325.663, + "vacancy_rate": 0.000, + "square_feet_per_unit": 762.500, + "percent": 0.1194164, + "efficiency": 0.9003, + "household_size": 2.500, + "floor_area_ratio": 0.0338032249, + "building_use_definition": 9, + "gross_built_up_area": 1472.468, + "built_form": 419 + } + }, + { + "pk": 1485, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2285625295, + "net_built_up_area": 843.601, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2409.300, + "percent": 0.0684156, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0193664180, + "building_use_definition": 26, + "gross_built_up_area": 843.601, + "built_form": 419 + } + }, + { + "pk": 1486, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1464555445, + "net_built_up_area": 130.375, + "vacancy_rate": 0.000, + "square_feet_per_unit": 890.200, + "percent": 0.0124392, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0035211669, + "building_use_definition": 14, + "gross_built_up_area": 153.382, + "built_form": 419 + } + }, + { + "pk": 1487, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0417204992, + "net_built_up_area": 20.860, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0019903, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0005633946, + "building_use_definition": 16, + "gross_built_up_area": 24.541, + "built_form": 419 + } + }, + { + "pk": 1488, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.3728555689, + "net_built_up_area": 1275.907, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3421.986, + "percent": 0.1214927, + "efficiency": 0.8517, + "household_size": 2.500, + "floor_area_ratio": 0.0343909636, + "building_use_definition": 8, + "gross_built_up_area": 1498.070, + "built_form": 419 + } + }, + { + "pk": 1489, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.7841847828, + "net_built_up_area": 123.997, + "vacancy_rate": 0.000, + "square_feet_per_unit": 25.918, + "percent": 0.2507743, + "efficiency": 0.0401, + "household_size": 2.500, + "floor_area_ratio": 0.0709867327, + "building_use_definition": 3, + "gross_built_up_area": 3092.182, + "built_form": 419 + } + }, + { + "pk": 1490, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2251919458, + "net_built_up_area": 123.856, + "vacancy_rate": 0.000, + "square_feet_per_unit": 550.000, + "percent": 0.0118172, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0033450972, + "building_use_definition": 17, + "gross_built_up_area": 145.712, + "built_form": 419 + } + }, + { + "pk": 1491, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1460206990, + "net_built_up_area": 73.010, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0069660, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0019718671, + "building_use_definition": 15, + "gross_built_up_area": 85.895, + "built_form": 419 + } + }, + { + "pk": 1492, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.0606144264, + "net_built_up_area": 3613.901, + "vacancy_rate": 0.000, + "square_feet_per_unit": 326.736, + "percent": 0.2654430, + "efficiency": 0.8160, + "household_size": 2.500, + "floor_area_ratio": 0.1016712613, + "building_use_definition": 5, + "gross_built_up_area": 4428.800, + "built_form": 420 + } + }, + { + "pk": 1493, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.9779873763, + "net_built_up_area": 2233.491, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.1407927, + "efficiency": 0.9508, + "household_size": 2.500, + "floor_area_ratio": 0.0539271007, + "building_use_definition": 7, + "gross_built_up_area": 2349.065, + "built_form": 420 + } + }, + { + "pk": 1494, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9320170351, + "net_built_up_area": 699.013, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0472811, + "efficiency": 0.8861, + "household_size": 2.500, + "floor_area_ratio": 0.0181098355, + "building_use_definition": 10, + "gross_built_up_area": 788.864, + "built_form": 420 + } + }, + { + "pk": 1495, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 5.9911202917, + "net_built_up_area": 4609.406, + "vacancy_rate": 0.000, + "square_feet_per_unit": 769.373, + "percent": 0.3106226, + "efficiency": 0.8894, + "household_size": 2.500, + "floor_area_ratio": 0.1189761701, + "building_use_definition": 9, + "gross_built_up_area": 5182.602, + "built_form": 420 + } + }, + { + "pk": 1496, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2600871250, + "net_built_up_area": 130.044, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0091697, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0035122228, + "building_use_definition": 16, + "gross_built_up_area": 152.992, + "built_form": 420 + } + }, + { + "pk": 1497, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0473331675, + "net_built_up_area": 23.667, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0014328, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0005487980, + "building_use_definition": 8, + "gross_built_up_area": 23.906, + "built_form": 420 + } + }, + { + "pk": 1498, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 9.6189302281, + "net_built_up_area": 4609.574, + "vacancy_rate": 0.000, + "square_feet_per_unit": 479.219, + "percent": 0.4986964, + "efficiency": 0.5540, + "household_size": 2.500, + "floor_area_ratio": 0.1910131063, + "building_use_definition": 3, + "gross_built_up_area": 8320.531, + "built_form": 420 + } + }, + { + "pk": 1499, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 10.6194480751, + "net_built_up_area": 3614.117, + "vacancy_rate": 0.000, + "square_feet_per_unit": 340.330, + "percent": 0.2548405, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0976102405, + "building_use_definition": 15, + "gross_built_up_area": 4251.902, + "built_form": 420 + } + }, + { + "pk": 1500, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.4783456054, + "net_built_up_area": 2194.613, + "vacancy_rate": 0.000, + "square_feet_per_unit": 630.936, + "percent": 0.1194503, + "efficiency": 0.4428, + "household_size": 2.500, + "floor_area_ratio": 0.1137791161, + "building_use_definition": 4, + "gross_built_up_area": 4956.218, + "built_form": 421 + } + }, + { + "pk": 1501, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8789344836, + "net_built_up_area": 3170.561, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1687.425, + "percent": 0.0764140, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0727860657, + "building_use_definition": 12, + "gross_built_up_area": 3170.561, + "built_form": 421 + } + }, + { + "pk": 1502, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.5966587408, + "net_built_up_area": 5096.735, + "vacancy_rate": 0.000, + "square_feet_per_unit": 374.852, + "percent": 0.1445140, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1376528581, + "building_use_definition": 5, + "gross_built_up_area": 5996.158, + "built_form": 421 + } + }, + { + "pk": 1503, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0021537055, + "net_built_up_area": 1.615, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0000458, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000436255, + "building_use_definition": 7, + "gross_built_up_area": 1.900, + "built_form": 421 + } + }, + { + "pk": 1504, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 16.4458224049, + "net_built_up_area": 14288.311, + "vacancy_rate": 0.000, + "square_feet_per_unit": 868.811, + "percent": 0.4815606, + "efficiency": 0.7151, + "household_size": 2.356, + "floor_area_ratio": 0.4586973782, + "building_use_definition": 2, + "gross_built_up_area": 19980.858, + "built_form": 421 + } + }, + { + "pk": 1505, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7978865175, + "net_built_up_area": 1662.263, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2083.333, + "percent": 0.0471322, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0448944880, + "building_use_definition": 20, + "gross_built_up_area": 1955.604, + "built_form": 421 + } + }, + { + "pk": 1506, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0718669903, + "net_built_up_area": 53.900, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0015283, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0014557404, + "building_use_definition": 13, + "gross_built_up_area": 63.412, + "built_form": 421 + } + }, + { + "pk": 1507, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.8366266460, + "net_built_up_area": 14288.752, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1032.676, + "percent": 0.4051466, + "efficiency": 0.8500, + "household_size": 2.329, + "floor_area_ratio": 0.3859113125, + "building_use_definition": 14, + "gross_built_up_area": 16810.297, + "built_form": 421 + } + }, + { + "pk": 1508, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.3623230107, + "net_built_up_area": 355.735, + "vacancy_rate": 0.000, + "square_feet_per_unit": 981.818, + "percent": 0.0100866, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0096077149, + "building_use_definition": 19, + "gross_built_up_area": 418.512, + "built_form": 421 + } + }, + { + "pk": 1509, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1755988537, + "net_built_up_area": 600.979, + "vacancy_rate": 0.000, + "square_feet_per_unit": 511.211, + "percent": 0.0170403, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0162312717, + "building_use_definition": 9, + "gross_built_up_area": 707.034, + "built_form": 421 + } + }, + { + "pk": 1510, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.2841608288, + "net_built_up_area": 600.963, + "vacancy_rate": 0.000, + "square_feet_per_unit": 467.981, + "percent": 0.0186144, + "efficiency": 0.7781, + "household_size": 2.500, + "floor_area_ratio": 0.0177306376, + "building_use_definition": 3, + "gross_built_up_area": 772.347, + "built_form": 421 + } + }, + { + "pk": 1511, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.8122993900, + "net_built_up_area": 2194.787, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1211.051, + "percent": 0.0622315, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0592769132, + "building_use_definition": 18, + "gross_built_up_area": 2582.102, + "built_form": 421 + } + }, + { + "pk": 1512, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 13.5966587408, + "net_built_up_area": 5096.735, + "vacancy_rate": 0.000, + "square_feet_per_unit": 374.852, + "percent": 0.1445140, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1376528581, + "building_use_definition": 15, + "gross_built_up_area": 5996.158, + "built_form": 421 + } + }, + { + "pk": 1513, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1540746873, + "net_built_up_area": 934.251, + "vacancy_rate": 0.000, + "square_feet_per_unit": 809.524, + "percent": 0.0401173, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0252323005, + "building_use_definition": 18, + "gross_built_up_area": 1099.119, + "built_form": 422 + } + }, + { + "pk": 1514, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.7214218418, + "net_built_up_area": 4605.832, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1692.436, + "percent": 0.1681106, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1057353603, + "building_use_definition": 12, + "gross_built_up_area": 4605.832, + "built_form": 422 + } + }, + { + "pk": 1515, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.6443476362, + "net_built_up_area": 2777.965, + "vacancy_rate": 0.000, + "square_feet_per_unit": 321.362, + "percent": 0.1195688, + "efficiency": 0.8480, + "household_size": 2.500, + "floor_area_ratio": 0.0752043604, + "building_use_definition": 5, + "gross_built_up_area": 3275.902, + "built_form": 422 + } + }, + { + "pk": 1516, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6677126696, + "net_built_up_area": 500.785, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0185304, + "efficiency": 0.9864, + "household_size": 2.500, + "floor_area_ratio": 0.0116549374, + "building_use_definition": 7, + "gross_built_up_area": 507.689, + "built_form": 422 + } + }, + { + "pk": 1517, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.9946672562, + "net_built_up_area": 4092.493, + "vacancy_rate": 0.000, + "square_feet_per_unit": 454.991, + "percent": 0.3438627, + "efficiency": 0.4344, + "household_size": 2.500, + "floor_area_ratio": 0.2162769420, + "building_use_definition": 2, + "gross_built_up_area": 9421.024, + "built_form": 422 + } + }, + { + "pk": 1518, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.8003552307, + "net_built_up_area": 4064.438, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1451.401, + "percent": 0.1745294, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1097725485, + "building_use_definition": 20, + "gross_built_up_area": 4781.692, + "built_form": 422 + } + }, + { + "pk": 1519, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0994433571, + "net_built_up_area": 74.583, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0028655, + "efficiency": 0.9500, + "household_size": 2.500, + "floor_area_ratio": 0.0018022937, + "building_use_definition": 10, + "gross_built_up_area": 78.508, + "built_form": 422 + } + }, + { + "pk": 1520, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7279849436, + "net_built_up_area": 531.683, + "vacancy_rate": 0.000, + "square_feet_per_unit": 730.349, + "percent": 0.0218735, + "efficiency": 0.8872, + "household_size": 2.500, + "floor_area_ratio": 0.0137576239, + "building_use_definition": 9, + "gross_built_up_area": 599.282, + "built_form": 422 + } + }, + { + "pk": 1521, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2372704309, + "net_built_up_area": 177.953, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0076414, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0048061585, + "building_use_definition": 13, + "gross_built_up_area": 209.356, + "built_form": 422 + } + }, + { + "pk": 1522, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 7.1855439205, + "net_built_up_area": 934.185, + "vacancy_rate": 0.000, + "square_feet_per_unit": 130.009, + "percent": 0.2497971, + "efficiency": 0.1365, + "household_size": 2.500, + "floor_area_ratio": 0.1571131528, + "building_use_definition": 4, + "gross_built_up_area": 6843.849, + "built_form": 422 + } + }, + { + "pk": 1523, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 4.5977450054, + "net_built_up_area": 4092.913, + "vacancy_rate": 0.000, + "square_feet_per_unit": 890.200, + "percent": 0.1757521, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.1105415817, + "building_use_definition": 14, + "gross_built_up_area": 4815.191, + "built_form": 422 + } + }, + { + "pk": 1524, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.9229106428, + "net_built_up_area": 818.582, + "vacancy_rate": 0.000, + "square_feet_per_unit": 886.957, + "percent": 0.0351504, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0221083038, + "building_use_definition": 19, + "gross_built_up_area": 963.038, + "built_form": 422 + } + }, + { + "pk": 1525, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0155472792, + "net_built_up_area": 7.774, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0002866, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0001802608, + "building_use_definition": 8, + "gross_built_up_area": 7.852, + "built_form": 422 + } + }, + { + "pk": 1526, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6944811300, + "net_built_up_area": 531.711, + "vacancy_rate": 0.000, + "square_feet_per_unit": 313.790, + "percent": 0.0509108, + "efficiency": 0.3812, + "household_size": 2.500, + "floor_area_ratio": 0.0320210134, + "building_use_definition": 3, + "gross_built_up_area": 1394.835, + "built_form": 422 + } + }, + { + "pk": 1527, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 8.6232509201, + "net_built_up_area": 2777.842, + "vacancy_rate": 0.000, + "square_feet_per_unit": 322.134, + "percent": 0.1192822, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0750240996, + "building_use_definition": 15, + "gross_built_up_area": 3268.050, + "built_form": 422 + } + }, + { + "pk": 1528, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1909259547, + "net_built_up_area": 731.883, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3833.333, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0197667251, + "building_use_definition": 5, + "gross_built_up_area": 861.039, + "built_form": 423 + } + }, + { + "pk": 1529, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.0363696609, + "net_built_up_area": 1755.144, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1693.550, + "percent": 0.0611521, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0402925583, + "building_use_definition": 12, + "gross_built_up_area": 1755.144, + "built_form": 423 + } + }, + { + "pk": 1530, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 18.5750417488, + "net_built_up_area": 15077.844, + "vacancy_rate": 0.000, + "square_feet_per_unit": 811.726, + "percent": 0.6791686, + "efficiency": 0.7735, + "household_size": 2.498, + "floor_area_ratio": 0.4474979666, + "building_use_definition": 2, + "gross_built_up_area": 19493.011, + "built_form": 423 + } + }, + { + "pk": 1531, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0145888631, + "net_built_up_area": 7.294, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0002990, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0001970084, + "building_use_definition": 9, + "gross_built_up_area": 8.582, + "built_form": 423 + } + }, + { + "pk": 1532, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 16.9018048951, + "net_built_up_area": 15077.187, + "vacancy_rate": 0.000, + "square_feet_per_unit": 892.046, + "percent": 0.6180165, + "efficiency": 0.8500, + "household_size": 2.498, + "floor_area_ratio": 0.4072054083, + "building_use_definition": 14, + "gross_built_up_area": 17737.868, + "built_form": 423 + } + }, + { + "pk": 1533, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1909259547, + "net_built_up_area": 731.883, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3833.333, + "percent": 0.0300000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0197667251, + "building_use_definition": 8, + "gross_built_up_area": 861.039, + "built_form": 423 + } + }, + { + "pk": 1534, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0145888631, + "net_built_up_area": 7.294, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0002990, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0001970084, + "building_use_definition": 3, + "gross_built_up_area": 8.582, + "built_form": 423 + } + }, + { + "pk": 1535, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4638118670, + "net_built_up_area": 3753.615, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3134.500, + "percent": 0.1818509, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0861711326, + "building_use_definition": 11, + "gross_built_up_area": 3753.615, + "built_form": 424 + } + }, + { + "pk": 1536, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2746172703, + "net_built_up_area": 1052.699, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3833.333, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0284313575, + "building_use_definition": 5, + "gross_built_up_area": 1238.470, + "built_form": 424 + } + }, + { + "pk": 1537, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.9991899708, + "net_built_up_area": 3351.442, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1676.400, + "percent": 0.1623669, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0769385231, + "building_use_definition": 12, + "gross_built_up_area": 3351.442, + "built_form": 424 + } + }, + { + "pk": 1538, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.2669092897, + "net_built_up_area": 2278.981, + "vacancy_rate": 0.000, + "square_feet_per_unit": 178.040, + "percent": 0.6494676, + "efficiency": 0.1700, + "household_size": 2.500, + "floor_area_ratio": 0.3077540925, + "building_use_definition": 2, + "gross_built_up_area": 13405.768, + "built_form": 424 + } + }, + { + "pk": 1539, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.5600766735, + "net_built_up_area": 2278.980, + "vacancy_rate": 0.000, + "square_feet_per_unit": 890.200, + "percent": 0.1298935, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0615508090, + "building_use_definition": 14, + "gross_built_up_area": 2681.153, + "built_form": 424 + } + }, + { + "pk": 1540, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2746172703, + "net_built_up_area": 1052.699, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3833.333, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0284313575, + "building_use_definition": 8, + "gross_built_up_area": 1238.470, + "built_form": 424 + } + }, + { + "pk": 1541, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.4638118670, + "net_built_up_area": 3619.558, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2322.393, + "percent": 0.1753563, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0830936277, + "building_use_definition": 26, + "gross_built_up_area": 3619.558, + "built_form": 424 + } + }, + { + "pk": 1542, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1884024657, + "net_built_up_area": 722.209, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3833.333, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0195054661, + "building_use_definition": 8, + "gross_built_up_area": 849.658, + "built_form": 425 + } + }, + { + "pk": 1543, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2132913305, + "net_built_up_area": 1839.418, + "vacancy_rate": 0.000, + "square_feet_per_unit": 481.860, + "percent": 0.6494675, + "efficiency": 0.2000, + "household_size": 2.500, + "floor_area_ratio": 0.2111361054, + "building_use_definition": 2, + "gross_built_up_area": 9197.089, + "built_form": 425 + } + }, + { + "pk": 1544, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2132913305, + "net_built_up_area": 7357.671, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3442.375, + "percent": 0.5195740, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1689088843, + "building_use_definition": 11, + "gross_built_up_area": 7357.671, + "built_form": 425 + } + }, + { + "pk": 1545, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2132913305, + "net_built_up_area": 1839.418, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2409.300, + "percent": 0.1298935, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0422272211, + "building_use_definition": 26, + "gross_built_up_area": 1839.418, + "built_form": 425 + } + }, + { + "pk": 1546, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1884024657, + "net_built_up_area": 722.209, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3833.333, + "percent": 0.0600000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0195054661, + "building_use_definition": 5, + "gross_built_up_area": 849.658, + "built_form": 425 + } + }, + { + "pk": 1547, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0597437311, + "net_built_up_area": 212.837, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3562.500, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0057483131, + "building_use_definition": 8, + "gross_built_up_area": 250.397, + "built_form": 426 + } + }, + { + "pk": 1548, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0533554777, + "net_built_up_area": 4604.973, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3494.180, + "percent": 0.7356289, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1057156316, + "building_use_definition": 2, + "gross_built_up_area": 4604.973, + "built_form": 426 + } + }, + { + "pk": 1549, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0533554777, + "net_built_up_area": 4604.973, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3494.180, + "percent": 0.7356289, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.1057156316, + "building_use_definition": 11, + "gross_built_up_area": 4604.973, + "built_form": 426 + } + }, + { + "pk": 1550, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0597437311, + "net_built_up_area": 212.837, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3562.500, + "percent": 0.0400000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0057483131, + "building_use_definition": 5, + "gross_built_up_area": 250.397, + "built_form": 426 + } + }, + { + "pk": 1551, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0003245457, + "net_built_up_area": 1.332, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4104.167, + "percent": 0.0012000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000359744, + "building_use_definition": 8, + "gross_built_up_area": 1.567, + "built_form": 427 + } + }, + { + "pk": 1552, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0053181033, + "net_built_up_area": 1109.544, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3355.620, + "percent": 0.8496578, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0254716361, + "building_use_definition": 2, + "gross_built_up_area": 1109.544, + "built_form": 427 + } + }, + { + "pk": 1553, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0053181033, + "net_built_up_area": 1109.544, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3355.620, + "percent": 0.8496578, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0254716361, + "building_use_definition": 11, + "gross_built_up_area": 1109.544, + "built_form": 427 + } + }, + { + "pk": 1554, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0003245457, + "net_built_up_area": 1.332, + "vacancy_rate": 0.000, + "square_feet_per_unit": 4104.167, + "percent": 0.0012000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000359744, + "building_use_definition": 5, + "gross_built_up_area": 1.567, + "built_form": 427 + } + }, + { + "pk": 1555, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0048955591, + "net_built_up_area": 249.578, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3440.375, + "percent": 0.7418566, + "efficiency": 0.9998, + "household_size": 2.497, + "floor_area_ratio": 0.0057306606, + "building_use_definition": 11, + "gross_built_up_area": 249.628, + "built_form": 428 + } + }, + { + "pk": 1556, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0048955591, + "net_built_up_area": 249.578, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3440.375, + "percent": 0.7418566, + "efficiency": 0.9998, + "household_size": 2.497, + "floor_area_ratio": 0.0057306606, + "building_use_definition": 2, + "gross_built_up_area": 249.628, + "built_form": 428 + } + }, + { + "pk": 1557, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0000163480, + "net_built_up_area": 0.064, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3887.500, + "percent": 0.0002222, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000017164, + "building_use_definition": 20, + "gross_built_up_area": 0.075, + "built_form": 428 + } + }, + { + "pk": 1558, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0003111392, + "net_built_up_area": 3.111, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.0092466, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0000714277, + "building_use_definition": 23, + "gross_built_up_area": 3.111, + "built_form": 428 + } + }, + { + "pk": 1559, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0000204394, + "net_built_up_area": 0.064, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3109.440, + "percent": 0.0002778, + "efficiency": 0.6799, + "household_size": 2.500, + "floor_area_ratio": 0.0000021459, + "building_use_definition": 4, + "gross_built_up_area": 0.093, + "built_form": 428 + } + }, + { + "pk": 1560, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0000132521, + "net_built_up_area": 0.016, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1200.000, + "percent": 0.0000556, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 4.295E-7, + "building_use_definition": 19, + "gross_built_up_area": 0.019, + "built_form": 428 + } + }, + { + "pk": 1561, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0061884089, + "net_built_up_area": 58.773, + "vacancy_rate": 0.000, + "square_feet_per_unit": 9497.235, + "percent": 0.1839150, + "efficiency": 0.9497, + "household_size": 2.500, + "floor_area_ratio": 0.0014206983, + "building_use_definition": 6, + "gross_built_up_area": 61.886, + "built_form": 428 + } + }, + { + "pk": 1562, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0058774228, + "net_built_up_area": 58.774, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.1746684, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0013492706, + "building_use_definition": 22, + "gross_built_up_area": 58.774, + "built_form": 428 + } + }, + { + "pk": 1563, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0079446855, + "net_built_up_area": 32.405, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3358.755, + "percent": 0.0794895, + "efficiency": 0.9915, + "household_size": 2.359, + "floor_area_ratio": 0.0007503015, + "building_use_definition": 11, + "gross_built_up_area": 32.683, + "built_form": 429 + } + }, + { + "pk": 1564, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0004013185, + "net_built_up_area": 0.080, + "vacancy_rate": 0.000, + "square_feet_per_unit": 200.017, + "percent": 0.0005742, + "efficiency": 0.3400, + "household_size": 2.500, + "floor_area_ratio": 0.0000054199, + "building_use_definition": 5, + "gross_built_up_area": 0.236, + "built_form": 429 + } + }, + { + "pk": 1565, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0120477873, + "net_built_up_area": 9.036, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0222793, + "efficiency": 0.9864, + "household_size": 2.500, + "floor_area_ratio": 0.0002102944, + "building_use_definition": 7, + "gross_built_up_area": 9.160, + "built_form": 429 + } + }, + { + "pk": 1566, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0079446855, + "net_built_up_area": 32.405, + "vacancy_rate": 0.000, + "square_feet_per_unit": 3358.755, + "percent": 0.0794895, + "efficiency": 0.9915, + "household_size": 2.359, + "floor_area_ratio": 0.0007503015, + "building_use_definition": 2, + "gross_built_up_area": 32.683, + "built_form": 429 + } + }, + { + "pk": 1567, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0017942821, + "net_built_up_area": 1.346, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0034452, + "efficiency": 0.9500, + "household_size": 2.500, + "floor_area_ratio": 0.0000325192, + "building_use_definition": 10, + "gross_built_up_area": 1.417, + "built_form": 429 + } + }, + { + "pk": 1568, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0096870986, + "net_built_up_area": 7.265, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0196379, + "efficiency": 0.8998, + "household_size": 2.500, + "floor_area_ratio": 0.0001853622, + "building_use_definition": 9, + "gross_built_up_area": 8.074, + "built_form": 429 + } + }, + { + "pk": 1569, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0016786672, + "net_built_up_area": 16.787, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.0408273, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0003853690, + "building_use_definition": 23, + "gross_built_up_area": 16.787, + "built_form": 429 + } + }, + { + "pk": 1570, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0002804583, + "net_built_up_area": 0.140, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0003445, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0000032517, + "building_use_definition": 8, + "gross_built_up_area": 0.142, + "built_form": 429 + } + }, + { + "pk": 1571, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0333878937, + "net_built_up_area": 317.093, + "vacancy_rate": 0.000, + "square_feet_per_unit": 9497.235, + "percent": 0.8120557, + "efficiency": 0.9497, + "household_size": 2.500, + "floor_area_ratio": 0.0076649954, + "building_use_definition": 6, + "gross_built_up_area": 333.887, + "built_form": 429 + } + }, + { + "pk": 1572, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0223746653, + "net_built_up_area": 7.265, + "vacancy_rate": 0.000, + "square_feet_per_unit": 324.684, + "percent": 0.0453624, + "efficiency": 0.3895, + "household_size": 2.500, + "floor_area_ratio": 0.0004281758, + "building_use_definition": 3, + "gross_built_up_area": 18.651, + "built_form": 429 + } + }, + { + "pk": 1573, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0317100527, + "net_built_up_area": 317.101, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.7712284, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0072796264, + "building_use_definition": 22, + "gross_built_up_area": 317.101, + "built_form": 429 + } + }, + { + "pk": 1574, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0001605550, + "net_built_up_area": 0.080, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0002297, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0000021681, + "building_use_definition": 15, + "gross_built_up_area": 0.094, + "built_form": 429 + } + }, + { + "pk": 1575, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 22.1148191404, + "net_built_up_area": 456.914, + "vacancy_rate": 0.000, + "square_feet_per_unit": 20.661, + "percent": 0.6050000, + "efficiency": 0.0281, + "household_size": 2.500, + "floor_area_ratio": 0.3732850000, + "building_use_definition": 5, + "gross_built_up_area": 16260.295, + "built_form": 430 + } + }, + { + "pk": 1576, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2741405040, + "net_built_up_area": 205.605, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0090000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0055530000, + "building_use_definition": 7, + "gross_built_up_area": 241.889, + "built_form": 430 + } + }, + { + "pk": 1577, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0376271280, + "net_built_up_area": 18.814, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0700000, + "efficiency": 0.0100, + "household_size": 2.500, + "floor_area_ratio": 0.0431900000, + "building_use_definition": 10, + "gross_built_up_area": 1881.356, + "built_form": 430 + } + }, + { + "pk": 1578, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1827603360, + "net_built_up_area": 137.070, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0060000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0037020000, + "building_use_definition": 9, + "gross_built_up_area": 161.259, + "built_form": 430 + } + }, + { + "pk": 1579, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7310413440, + "net_built_up_area": 456.901, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.000, + "percent": 0.0200000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0123400000, + "building_use_definition": 16, + "gross_built_up_area": 537.530, + "built_form": 430 + } + }, + { + "pk": 1580, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 21.3610514177, + "net_built_up_area": 13364.350, + "vacancy_rate": 0.000, + "square_feet_per_unit": 625.641, + "percent": 0.5850000, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.3609450000, + "building_use_definition": 8, + "gross_built_up_area": 15722.764, + "built_form": 430 + } + }, + { + "pk": 1581, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.5891133904, + "net_built_up_area": 137.070, + "vacancy_rate": 0.000, + "square_feet_per_unit": 52.941, + "percent": 0.0850000, + "efficiency": 0.0600, + "household_size": 2.500, + "floor_area_ratio": 0.0524450000, + "building_use_definition": 3, + "gross_built_up_area": 2284.504, + "built_form": 430 + } + }, + { + "pk": 1582, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6186671447, + "net_built_up_area": 760.845, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1229.813, + "percent": 0.0131902, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0205489358, + "building_use_definition": 18, + "gross_built_up_area": 895.112, + "built_form": 431 + } + }, + { + "pk": 1583, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.0343665979, + "net_built_up_area": 10.694, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0001854, + "efficiency": 0.8500, + "household_size": 0.000, + "floor_area_ratio": 0.0002888336, + "building_use_definition": 11, + "gross_built_up_area": 12.582, + "built_form": 431 + } + }, + { + "pk": 1584, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 83.9137047213, + "net_built_up_area": 3574.892, + "vacancy_rate": 0.000, + "square_feet_per_unit": 42.602, + "percent": 0.4625017, + "efficiency": 0.1139, + "household_size": 2.500, + "floor_area_ratio": 0.7205287064, + "building_use_definition": 5, + "gross_built_up_area": 31386.230, + "built_form": 431 + } + }, + { + "pk": 1585, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.7338731540, + "net_built_up_area": 519.656, + "vacancy_rate": 0.000, + "square_feet_per_unit": 708.101, + "percent": 0.0080445, + "efficiency": 0.9519, + "household_size": 2.500, + "floor_area_ratio": 0.0125324797, + "building_use_definition": 7, + "gross_built_up_area": 545.915, + "built_form": 431 + } + }, + { + "pk": 1586, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.6252441318, + "net_built_up_area": 959.188, + "vacancy_rate": 0.000, + "square_feet_per_unit": 380.209, + "percent": 0.0392841, + "efficiency": 0.3598, + "household_size": 2.156, + "floor_area_ratio": 0.0612004707, + "building_use_definition": 2, + "gross_built_up_area": 2665.893, + "built_form": 431 + } + }, + { + "pk": 1587, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 36.9666141407, + "net_built_up_area": 23098.959, + "vacancy_rate": 0.000, + "square_feet_per_unit": 624.860, + "percent": 0.4004494, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.6238577894, + "building_use_definition": 16, + "gross_built_up_area": 27175.245, + "built_form": 431 + } + }, + { + "pk": 1588, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.6833197916, + "net_built_up_area": 1396.294, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2043.397, + "percent": 0.0242065, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0377111655, + "building_use_definition": 20, + "gross_built_up_area": 1642.698, + "built_form": 431 + } + }, + { + "pk": 1589, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1216961104, + "net_built_up_area": 84.251, + "vacancy_rate": 0.000, + "square_feet_per_unit": 692.308, + "percent": 0.0014606, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0022754602, + "building_use_definition": 13, + "gross_built_up_area": 99.119, + "built_form": 431 + } + }, + { + "pk": 1590, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.1877594790, + "net_built_up_area": 96.432, + "vacancy_rate": 0.000, + "square_feet_per_unit": 513.593, + "percent": 0.0237626, + "efficiency": 0.0598, + "household_size": 2.500, + "floor_area_ratio": 0.0370196162, + "building_use_definition": 10, + "gross_built_up_area": 1612.574, + "built_form": 431 + } + }, + { + "pk": 1591, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.1703875014, + "net_built_up_area": 877.791, + "vacancy_rate": 0.000, + "square_feet_per_unit": 750.000, + "percent": 0.0144654, + "efficiency": 0.8942, + "household_size": 2.500, + "floor_area_ratio": 0.0225355625, + "building_use_definition": 9, + "gross_built_up_area": 981.649, + "built_form": 431 + } + }, + { + "pk": 1592, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0114367610, + "net_built_up_area": 114.368, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.0016853, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0026255191, + "building_use_definition": 23, + "gross_built_up_area": 114.368, + "built_form": 431 + } + }, + { + "pk": 1593, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.9544049108, + "net_built_up_area": 760.891, + "vacancy_rate": 0.000, + "square_feet_per_unit": 389.321, + "percent": 0.0416661, + "efficiency": 0.2691, + "household_size": 2.500, + "floor_area_ratio": 0.0649113747, + "building_use_definition": 4, + "gross_built_up_area": 2827.539, + "built_form": 431 + } + }, + { + "pk": 1594, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 1.0678153253, + "net_built_up_area": 959.158, + "vacancy_rate": 0.000, + "square_feet_per_unit": 898.243, + "percent": 0.0166282, + "efficiency": 0.8500, + "household_size": 1.715, + "floor_area_ratio": 0.0259049760, + "building_use_definition": 14, + "gross_built_up_area": 1128.421, + "built_form": 431 + } + }, + { + "pk": 1595, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2437048124, + "net_built_up_area": 246.270, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1010.526, + "percent": 0.0042694, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0066512734, + "building_use_definition": 19, + "gross_built_up_area": 289.729, + "built_form": 431 + } + }, + { + "pk": 1596, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0113270962, + "net_built_up_area": 5.664, + "vacancy_rate": 0.000, + "square_feet_per_unit": 500.000, + "percent": 0.0000843, + "efficiency": 0.9900, + "household_size": 2.500, + "floor_area_ratio": 0.0001313305, + "building_use_definition": 8, + "gross_built_up_area": 5.721, + "built_form": 431 + } + }, + { + "pk": 1597, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2274701474, + "net_built_up_area": 2160.337, + "vacancy_rate": 0.000, + "square_feet_per_unit": 9497.232, + "percent": 0.0335204, + "efficiency": 0.9497, + "household_size": 2.500, + "floor_area_ratio": 0.0522212361, + "building_use_definition": 6, + "gross_built_up_area": 2274.757, + "built_form": 431 + } + }, + { + "pk": 1598, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 3.8622650257, + "net_built_up_area": 877.839, + "vacancy_rate": 0.000, + "square_feet_per_unit": 227.286, + "percent": 0.0477331, + "efficiency": 0.2710, + "household_size": 2.500, + "floor_area_ratio": 0.0743631187, + "building_use_definition": 3, + "gross_built_up_area": 3239.257, + "built_form": 431 + } + }, + { + "pk": 1599, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.2160389432, + "net_built_up_area": 2160.389, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.0318351, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0495957170, + "building_use_definition": 22, + "gross_built_up_area": 2160.389, + "built_form": 431 + } + }, + { + "pk": 1600, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 2.0343665979, + "net_built_up_area": 1524.890, + "vacancy_rate": 0.000, + "square_feet_per_unit": 2409.300, + "percent": 0.0224705, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0350066612, + "building_use_definition": 26, + "gross_built_up_area": 1524.890, + "built_form": 431 + } + }, + { + "pk": 1601, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 11.2418652926, + "net_built_up_area": 3574.475, + "vacancy_rate": 0.000, + "square_feet_per_unit": 317.961, + "percent": 0.0619680, + "efficiency": 0.8500, + "household_size": 2.500, + "floor_area_ratio": 0.0965395865, + "building_use_definition": 15, + "gross_built_up_area": 4205.264, + "built_form": 431 + } + }, + { + "pk": 1602, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0000021711, + "net_built_up_area": 0.022, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.0045762, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 4.984E-7, + "building_use_definition": 23, + "gross_built_up_area": 0.022, + "built_form": 432 + } + }, + { + "pk": 1603, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 72.1610204589, + "net_built_up_area": 0.002, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0005034, + "efficiency": 0.8500, + "household_size": 0.000, + "floor_area_ratio": 5.48E-8, + "building_use_definition": 11, + "gross_built_up_area": 0.002, + "built_form": 432 + } + }, + { + "pk": 1604, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0000410125, + "net_built_up_area": 0.410, + "vacancy_rate": 0.000, + "square_feet_per_unit": 10000.000, + "percent": 0.0864454, + "efficiency": 1.0000, + "household_size": 2.500, + "floor_area_ratio": 0.0000094152, + "building_use_definition": 22, + "gross_built_up_area": 0.410, + "built_form": 432 + } + }, + { + "pk": 1605, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 0.0000431825, + "net_built_up_area": 0.410, + "vacancy_rate": 0.000, + "square_feet_per_unit": 9497.240, + "percent": 0.0910216, + "efficiency": 0.9497, + "household_size": 2.500, + "floor_area_ratio": 0.0000099136, + "building_use_definition": 6, + "gross_built_up_area": 0.432, + "built_form": 432 + } + }, + { + "pk": 1606, + "model": "main.buildingusepercent", + "fields": { + "unit_density": 72.1610204589, + "net_built_up_area": 0.002, + "vacancy_rate": 0.000, + "square_feet_per_unit": 1000.000, + "percent": 0.0005034, + "efficiency": 0.8500, + "household_size": 0.000, + "floor_area_ratio": 5.48E-8, + "building_use_definition": 2, + "gross_built_up_area": 0.002, + "built_form": 432 + } + } +] \ No newline at end of file diff --git a/footprint/client/configuration/default/built_form/json_fixtures/infrastructuretypes.json b/footprint/client/configuration/default/built_form/json_fixtures/infrastructuretypes.json new file mode 100644 index 000000000..e6b63604c --- /dev/null +++ b/footprint/client/configuration/default/built_form/json_fixtures/infrastructuretypes.json @@ -0,0 +1,131 @@ +[ + { + "pk": 6, + "model": "main.medium", + "fields": { + "key": "built_form_1", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 7, + "model": "main.medium", + "fields": { + "key": "built_form_2", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 8, + "model": "main.medium", + "fields": { + "key": "built_form_3", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 1, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 2, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 3, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 1, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 6, + "name": "Park", + "tags": [], + "description": null + } + }, + { + "pk": 2, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 7, + "name": "Streets", + "tags": [], + "description": null + } + }, + { + "pk": 3, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 8, + "name": "Detention/Utilities", + "tags": [], + "description": null + } + }, + { + "pk": 1, + "model": "main.placetypecomponent", + "fields": { + "tags": [] + } + }, + { + "pk": 2, + "model": "main.placetypecomponent", + "fields": { + "tags": [] + } + }, + { + "pk": 3, + "model": "main.placetypecomponent", + "fields": { + "tags": [] + } + }, + { + "pk": 1, + "model": "main.infrastructuretype", + "fields": { + "infrastructureattributeset_ptr": 1, + "tags": [] + } + }, + { + "pk": 2, + "model": "main.infrastructuretype", + "fields": { + "infrastructureattributeset_ptr": 2, + "tags": [] + } + }, + { + "pk": 3, + "model": "main.infrastructuretype", + "fields": { + "infrastructureattributeset_ptr": 3, + "tags": [] + } + } +] \ No newline at end of file diff --git a/footprint/client/configuration/default/built_form/json_fixtures/placetype_components.json b/footprint/client/configuration/default/built_form/json_fixtures/placetype_components.json new file mode 100644 index 000000000..b6c9fd887 --- /dev/null +++ b/footprint/client/configuration/default/built_form/json_fixtures/placetype_components.json @@ -0,0 +1,7993 @@ +[ + { + "pk": 1, + "model": "main.tag", + "fields": { + "tag": "Mixed Use" + } + }, + { + "pk": 2, + "model": "main.tag", + "fields": { + "tag": "Civic" + } + }, + { + "pk": 3, + "model": "main.tag", + "fields": { + "tag": "Institutional" + } + }, + { + "pk": 4, + "model": "main.tag", + "fields": { + "tag": "Office/Industrial" + } + }, + { + "pk": 5, + "model": "main.tag", + "fields": { + "tag": "Residential" + } + }, + { + "pk": 6, + "model": "main.tag", + "fields": { + "tag": "Commercial/Retail" + } + }, + { + "pk": 8, + "model": "main.tag", + "fields": { + "tag": "Unsorted" + } + }, + { + "pk": 15, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Office Services", + "description": null + } + }, + { + "pk": 17, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Accommodation", + "description": null + } + }, + { + "pk": 3, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Retail", + "description": null + } + }, + { + "pk": 8, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Education Services", + "description": null + } + }, + { + "pk": 14, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Multifamily 5 Plus", + "description": null + } + }, + { + "pk": 13, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Arts Entertainment", + "description": null + } + }, + { + "pk": 9, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Retail Services", + "description": null + } + }, + { + "pk": 2, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Residential", + "description": null + } + }, + { + "pk": 7, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Restaurant", + "description": null + } + }, + { + "pk": 5, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Office", + "description": null + } + }, + { + "pk": 12, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Attached Single Family", + "description": null + } + }, + { + "pk": 16, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Medical Services", + "description": null + } + }, + { + "pk": 10, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Other Services", + "description": null + } + }, + { + "pk": 21, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Multifamily 2 To 4", + "description": null + } + }, + { + "pk": 26, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Single Family Small Lot", + "description": null + } + }, + { + "pk": 18, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Manufacturing", + "description": null + } + }, + { + "pk": 20, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Transport Warehouse", + "description": null + } + }, + { + "pk": 19, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Wholesale", + "description": null + } + }, + { + "pk": 4, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Industrial", + "description": null + } + }, + { + "pk": 22, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Agriculture", + "description": null + } + }, + { + "pk": 6, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Agricultural", + "description": null + } + }, + { + "pk": 11, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Single Family Large Lot", + "description": null + } + }, + { + "pk": 23, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Extraction", + "description": null + } + }, + { + "pk": 6, + "model": "main.medium", + "fields": { + "key": "built_form_1", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 7, + "model": "main.medium", + "fields": { + "key": "built_form_2", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 8, + "model": "main.medium", + "fields": { + "key": "built_form_3", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 246, + "model": "main.medium", + "fields": { + "key": "built_form_343", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGNTI1MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 247, + "model": "main.medium", + "fields": { + "key": "built_form_344", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzY2RkZGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 248, + "model": "main.medium", + "fields": { + "key": "built_form_345", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 249, + "model": "main.medium", + "fields": { + "key": "built_form_346", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzM2NUY5MXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 250, + "model": "main.medium", + "fields": { + "key": "built_form_347", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 251, + "model": "main.medium", + "fields": { + "key": "built_form_348", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 252, + "model": "main.medium", + "fields": { + "key": "built_form_349", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzAwOTk5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 253, + "model": "main.medium", + "fields": { + "key": "built_form_350", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 254, + "model": "main.medium", + "fields": { + "key": "built_form_351", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzVEMjY4MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 255, + "model": "main.medium", + "fields": { + "key": "built_form_352", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 256, + "model": "main.medium", + "fields": { + "key": "built_form_353", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZDRkU2RnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 257, + "model": "main.medium", + "fields": { + "key": "built_form_354", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZDRkU2RnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 258, + "model": "main.medium", + "fields": { + "key": "built_form_355", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NDNTUwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 259, + "model": "main.medium", + "fields": { + "key": "built_form_356", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 260, + "model": "main.medium", + "fields": { + "key": "built_form_357", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzAwRkZGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 261, + "model": "main.medium", + "fields": { + "key": "built_form_358", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzVEMjY4MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 262, + "model": "main.medium", + "fields": { + "key": "built_form_359", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGNjY5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 263, + "model": "main.medium", + "fields": { + "key": "built_form_360", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2MTQxNHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 264, + "model": "main.medium", + "fields": { + "key": "built_form_361", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzg3NjRBNnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 265, + "model": "main.medium", + "fields": { + "key": "built_form_362", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzVEMjY4MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 266, + "model": "main.medium", + "fields": { + "key": "built_form_363", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzg2MkM5MHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 267, + "model": "main.medium", + "fields": { + "key": "built_form_364", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 268, + "model": "main.medium", + "fields": { + "key": "built_form_366", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOTAxQXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 271, + "model": "main.medium", + "fields": { + "key": "built_form_369", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0RDQzE1NnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 272, + "model": "main.medium", + "fields": { + "key": "built_form_370", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk0OEE1NHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 273, + "model": "main.medium", + "fields": { + "key": "built_form_371", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOTAxQXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 275, + "model": "main.medium", + "fields": { + "key": "built_form_373", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZFQ0Y1OHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 278, + "model": "main.medium", + "fields": { + "key": "built_form_376", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZDRkU2RnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 279, + "model": "main.medium", + "fields": { + "key": "built_form_377", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0VFRTVGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 280, + "model": "main.medium", + "fields": { + "key": "built_form_378", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzg3NjRBNnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 361, + "model": "main.medium", + "fields": { + "key": "built_form_342", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQjBBRHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 422, + "model": "main.medium", + "fields": { + "key": "built_form_365", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOUI5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 423, + "model": "main.medium", + "fields": { + "key": "built_form_379", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQTczM3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 424, + "model": "main.medium", + "fields": { + "key": "built_form_380", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NDNTUwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 425, + "model": "main.medium", + "fields": { + "key": "built_form_381", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOTAxQXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 426, + "model": "main.medium", + "fields": { + "key": "built_form_382", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 427, + "model": "main.medium", + "fields": { + "key": "built_form_383", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 428, + "model": "main.medium", + "fields": { + "key": "built_form_384", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk0MDAwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 429, + "model": "main.medium", + "fields": { + "key": "built_form_385", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 431, + "model": "main.medium", + "fields": { + "key": "built_form_387", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 432, + "model": "main.medium", + "fields": { + "key": "built_form_388", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQTczM3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 433, + "model": "main.medium", + "fields": { + "key": "built_form_389", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0JDRDM5N3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 434, + "model": "main.medium", + "fields": { + "key": "built_form_390", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0E5ODFENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 435, + "model": "main.medium", + "fields": { + "key": "built_form_391", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0Y2NzlBQnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 436, + "model": "main.medium", + "fields": { + "key": "built_form_392", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGNTI1MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 439, + "model": "main.medium", + "fields": { + "key": "built_form_395", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzM2NUY5MXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 440, + "model": "main.medium", + "fields": { + "key": "built_form_396", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0RDNDFCOHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 441, + "model": "main.medium", + "fields": { + "key": "built_form_397", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0YxQzNDM3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 442, + "model": "main.medium", + "fields": { + "key": "built_form_398", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 443, + "model": "main.medium", + "fields": { + "key": "built_form_399", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 444, + "model": "main.medium", + "fields": { + "key": "built_form_400", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 362, + "model": "main.medium", + "fields": { + "key": "built_form_414", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQkE0MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 363, + "model": "main.medium", + "fields": { + "key": "built_form_415", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzg3NjRBNnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 364, + "model": "main.medium", + "fields": { + "key": "built_form_416", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0E5ODFENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 365, + "model": "main.medium", + "fields": { + "key": "built_form_417", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NGOURFQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 366, + "model": "main.medium", + "fields": { + "key": "built_form_418", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 367, + "model": "main.medium", + "fields": { + "key": "built_form_419", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0VFRTVGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 368, + "model": "main.medium", + "fields": { + "key": "built_form_420", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0Y2NzlBQnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 369, + "model": "main.medium", + "fields": { + "key": "built_form_421", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOUI5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 370, + "model": "main.medium", + "fields": { + "key": "built_form_422", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQjBBRHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 371, + "model": "main.medium", + "fields": { + "key": "built_form_423", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQ0NDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 372, + "model": "main.medium", + "fields": { + "key": "built_form_424", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZCQzc4MXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 373, + "model": "main.medium", + "fields": { + "key": "built_form_425", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZDREJBRXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 374, + "model": "main.medium", + "fields": { + "key": "built_form_426", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZFQ0Y1OHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 375, + "model": "main.medium", + "fields": { + "key": "built_form_427", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZFRTU2N3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 376, + "model": "main.medium", + "fields": { + "key": "built_form_428", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZFRjI5NXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 377, + "model": "main.medium", + "fields": { + "key": "built_form_429", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZFRjZBOXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 378, + "model": "main.medium", + "fields": { + "key": "built_form_430", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 379, + "model": "main.medium", + "fields": { + "key": "built_form_431", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0RDQzE1NnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 380, + "model": "main.medium", + "fields": { + "key": "built_form_432", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0JDRDM5N3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 381, + "model": "main.medium", + "fields": { + "key": "built_form_433", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzM2NUY5MXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 382, + "model": "main.medium", + "fields": { + "key": "built_form_434", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzUzOERENXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 404, + "model": "main.medium", + "fields": { + "key": "built_form_435", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzdEQkQ0Q3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 445, + "model": "main.medium", + "fields": { + "key": "built_form_401", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk0MDAwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 446, + "model": "main.medium", + "fields": { + "key": "built_form_402", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NDNTUwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 447, + "model": "main.medium", + "fields": { + "key": "built_form_403", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzVEMjY4MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 448, + "model": "main.medium", + "fields": { + "key": "built_form_404", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0MyMDAwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 449, + "model": "main.medium", + "fields": { + "key": "built_form_405", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2NkEwNXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 450, + "model": "main.medium", + "fields": { + "key": "built_form_406", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzg2MkM5MHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 451, + "model": "main.medium", + "fields": { + "key": "built_form_407", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0U2MTQxNHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 452, + "model": "main.medium", + "fields": { + "key": "built_form_408", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZBN0UwQXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 453, + "model": "main.medium", + "fields": { + "key": "built_form_409", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0IxMkY5Q3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 454, + "model": "main.medium", + "fields": { + "key": "built_form_410", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGNTI1MnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 455, + "model": "main.medium", + "fields": { + "key": "built_form_411", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOTAxQXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 456, + "model": "main.medium", + "fields": { + "key": "built_form_412", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0RDNDFCOHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 457, + "model": "main.medium", + "fields": { + "key": "built_form_413", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQTczM3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 399, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 153.520, + "combined_pop_emp_density": 2.7369, + "residential_irrigated_square_feet": 18.41, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.343, + "impervious_roof_percent": 0.328, + "softscape_and_landscape_percent": 0.007, + "impervious_hardscape_percent": 0.015, + "parking_structure_square_feet": 49649.79, + "gross_net_ratio": 0.5536478, + "irrigated_percent": 0.136, + "household_density": 0.474, + "total_far": 4.4704578, + "floors": 11.895, + "residential_average_lot_size": 1316.63, + "commercial_irrigated_square_feet": 3.10, + "intersection_density": 0.0000, + "gross_population_density": 1.137 + } + }, + { + "pk": 402, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 47.460, + "combined_pop_emp_density": 1.4930, + "residential_irrigated_square_feet": 19.92, + "pervious_hardscape_percent": 0.004, + "hardscape_percent": 0.280, + "impervious_roof_percent": 0.265, + "softscape_and_landscape_percent": 0.007, + "impervious_hardscape_percent": 0.015, + "parking_structure_square_feet": 15296.89, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.138, + "household_density": 0.469, + "total_far": 1.4487550, + "floors": 3.073, + "residential_average_lot_size": 596.35, + "commercial_irrigated_square_feet": 3.85, + "intersection_density": 0.0000, + "gross_population_density": 1.118 + } + }, + { + "pk": 404, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 51.981, + "combined_pop_emp_density": 5.0012, + "residential_irrigated_square_feet": 21.44, + "pervious_hardscape_percent": 0.005, + "hardscape_percent": 0.349, + "impervious_roof_percent": 0.331, + "softscape_and_landscape_percent": 0.009, + "impervious_hardscape_percent": 0.018, + "parking_structure_square_feet": 15607.60, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.214, + "household_density": 0.244, + "total_far": 0.9682880, + "floors": 1.827, + "residential_average_lot_size": 1622.12, + "commercial_irrigated_square_feet": 27.85, + "intersection_density": 0.0000, + "gross_population_density": 0.442 + } + }, + { + "pk": 407, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 19.229, + "combined_pop_emp_density": 3.0751, + "residential_irrigated_square_feet": 41.18, + "pervious_hardscape_percent": 0.007, + "hardscape_percent": 0.320, + "impervious_roof_percent": 0.287, + "softscape_and_landscape_percent": 0.014, + "impervious_hardscape_percent": 0.033, + "parking_structure_square_feet": 2732.24, + "gross_net_ratio": 0.5701998, + "irrigated_percent": 0.277, + "household_density": 0.245, + "total_far": 0.6036295, + "floors": 1.296, + "residential_average_lot_size": 28921.60, + "commercial_irrigated_square_feet": 54.47, + "intersection_density": 0.0000, + "gross_population_density": 0.531 + } + }, + { + "pk": 409, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 21.153, + "combined_pop_emp_density": 3.1545, + "residential_irrigated_square_feet": 3.80, + "pervious_hardscape_percent": 0.011, + "hardscape_percent": 0.337, + "impervious_roof_percent": 0.261, + "softscape_and_landscape_percent": 0.023, + "impervious_hardscape_percent": 0.076, + "parking_structure_square_feet": 1365.59, + "gross_net_ratio": 0.4301998, + "irrigated_percent": 0.192, + "household_density": 0.020, + "total_far": 0.3838528, + "floors": 0.831, + "residential_average_lot_size": 1188.79, + "commercial_irrigated_square_feet": 78.81, + "intersection_density": 0.0000, + "gross_population_density": 0.014 + } + }, + { + "pk": 412, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 69.254, + "combined_pop_emp_density": 26.0294, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.007, + "hardscape_percent": 0.180, + "impervious_roof_percent": 0.149, + "softscape_and_landscape_percent": 0.012, + "impervious_hardscape_percent": 0.032, + "parking_structure_square_feet": 8594.37, + "gross_net_ratio": 0.6988970, + "irrigated_percent": 0.320, + "household_density": 0.000, + "total_far": 0.6921730, + "floors": 2.832, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 116.64, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 414, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 21.318, + "combined_pop_emp_density": 4.2630, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.026, + "hardscape_percent": 0.665, + "impervious_roof_percent": 0.489, + "softscape_and_landscape_percent": 0.027, + "impervious_hardscape_percent": 0.177, + "parking_structure_square_feet": 527.56, + "gross_net_ratio": 0.7564463, + "irrigated_percent": 0.247, + "household_density": 0.000, + "total_far": 0.4051072, + "floors": 1.051, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 220.08, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 416, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 6.049, + "combined_pop_emp_density": 0.5997, + "residential_irrigated_square_feet": 0.01, + "pervious_hardscape_percent": 0.005, + "hardscape_percent": 0.412, + "impervious_roof_percent": 0.320, + "softscape_and_landscape_percent": 0.002, + "impervious_hardscape_percent": 0.093, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.177, + "household_density": 0.001, + "total_far": 0.1402338, + "floors": 0.722, + "residential_average_lot_size": 905.24, + "commercial_irrigated_square_feet": 7.69, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 419, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 23.706, + "combined_pop_emp_density": 2.4584, + "residential_irrigated_square_feet": 329.60, + "pervious_hardscape_percent": 0.019, + "hardscape_percent": 0.391, + "impervious_roof_percent": 0.250, + "softscape_and_landscape_percent": 0.047, + "impervious_hardscape_percent": 0.141, + "parking_structure_square_feet": 898.12, + "gross_net_ratio": 0.7419603, + "irrigated_percent": 0.449, + "household_density": 0.361, + "total_far": 0.2830702, + "floors": 1.263, + "residential_average_lot_size": 190582.42, + "commercial_irrigated_square_feet": 348.32, + "intersection_density": 0.0000, + "gross_population_density": 0.902 + } + }, + { + "pk": 421, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 54.964, + "combined_pop_emp_density": 3.5389, + "residential_irrigated_square_feet": 28.63, + "pervious_hardscape_percent": 0.005, + "hardscape_percent": 0.426, + "impervious_roof_percent": 0.383, + "softscape_and_landscape_percent": 0.008, + "impervious_hardscape_percent": 0.043, + "parking_structure_square_feet": 13933.91, + "gross_net_ratio": 0.7641394, + "irrigated_percent": 0.166, + "household_density": 0.482, + "total_far": 0.9525226, + "floors": 2.395, + "residential_average_lot_size": 101.45, + "commercial_irrigated_square_feet": 16.80, + "intersection_density": 0.0000, + "gross_population_density": 1.135 + } + }, + { + "pk": 424, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 27.182, + "combined_pop_emp_density": 1.6401, + "residential_irrigated_square_feet": 122.65, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.186, + "impervious_roof_percent": 0.159, + "softscape_and_landscape_percent": 0.014, + "impervious_hardscape_percent": 0.026, + "parking_structure_square_feet": 5825.69, + "gross_net_ratio": 0.7094675, + "irrigated_percent": 0.312, + "household_density": 0.649, + "total_far": 0.4738560, + "floors": 1.662, + "residential_average_lot_size": 93917.39, + "commercial_irrigated_square_feet": 11.33, + "intersection_density": 0.0000, + "gross_population_density": 1.624 + } + }, + { + "pk": 426, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 5.243, + "combined_pop_emp_density": 1.8415, + "residential_irrigated_square_feet": 34.53, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.073, + "impervious_roof_percent": 0.059, + "softscape_and_landscape_percent": 0.004, + "impervious_hardscape_percent": 0.014, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.7756289, + "irrigated_percent": 0.269, + "household_density": 0.736, + "total_far": 0.1437078, + "floors": 1.555, + "residential_average_lot_size": 816411.02, + "commercial_irrigated_square_feet": 1.88, + "intersection_density": 0.0000, + "gross_population_density": 1.839 + } + }, + { + "pk": 429, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 2.705, + "combined_pop_emp_density": 0.2156, + "residential_irrigated_square_feet": 0.38, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.039, + "impervious_roof_percent": 0.022, + "softscape_and_landscape_percent": 0.005, + "impervious_hardscape_percent": 0.017, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.9374819, + "irrigated_percent": 0.024, + "household_density": 0.079, + "total_far": 0.0094390, + "floors": 1.015, + "residential_average_lot_size": 5482910.56, + "commercial_irrigated_square_feet": 0.22, + "intersection_density": 0.0000, + "gross_population_density": 0.188 + } + }, + { + "pk": 432, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.001, + "combined_pop_emp_density": 0.0000, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.0915250, + "irrigated_percent": 0.000, + "household_density": 0.001, + "total_far": 0.0001089, + "floors": 0.092, + "residential_average_lot_size": 603.65, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 398, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 260.102, + "combined_pop_emp_density": 26.8589, + "residential_irrigated_square_feet": 14.05, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.361, + "impervious_roof_percent": 0.351, + "softscape_and_landscape_percent": 0.005, + "impervious_hardscape_percent": 0.010, + "parking_structure_square_feet": 84684.20, + "gross_net_ratio": 0.5536478, + "irrigated_percent": 0.169, + "household_density": 0.379, + "total_far": 8.1344960, + "floors": 23.085, + "residential_average_lot_size": 2390.15, + "commercial_irrigated_square_feet": 6.45, + "intersection_density": 0.0000, + "gross_population_density": 0.837 + } + }, + { + "pk": 400, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 125.406, + "combined_pop_emp_density": 65.1471, + "residential_irrigated_square_feet": 1.90, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.265, + "impervious_roof_percent": 0.255, + "softscape_and_landscape_percent": 0.004, + "impervious_hardscape_percent": 0.010, + "parking_structure_square_feet": 39446.12, + "gross_net_ratio": 0.5536477, + "irrigated_percent": 0.199, + "household_density": 0.059, + "total_far": 3.5493744, + "floors": 8.337, + "residential_average_lot_size": 1078.71, + "commercial_irrigated_square_feet": 15.92, + "intersection_density": 0.0000, + "gross_population_density": 0.069 + } + }, + { + "pk": 401, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 79.275, + "combined_pop_emp_density": 11.7098, + "residential_irrigated_square_feet": 14.12, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.321, + "impervious_roof_percent": 0.308, + "softscape_and_landscape_percent": 0.007, + "impervious_hardscape_percent": 0.013, + "parking_structure_square_feet": 25741.74, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.186, + "household_density": 0.267, + "total_far": 1.9258753, + "floors": 3.848, + "residential_average_lot_size": 640.61, + "commercial_irrigated_square_feet": 15.52, + "intersection_density": 0.0000, + "gross_population_density": 0.526 + } + }, + { + "pk": 403, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 91.812, + "combined_pop_emp_density": 18.6219, + "residential_irrigated_square_feet": 0.67, + "pervious_hardscape_percent": 0.001, + "hardscape_percent": 0.318, + "impervious_roof_percent": 0.312, + "softscape_and_landscape_percent": 0.003, + "impervious_hardscape_percent": 0.006, + "parking_structure_square_feet": 25825.37, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.212, + "household_density": 0.028, + "total_far": 1.4699825, + "floors": 2.677, + "residential_average_lot_size": 1994.29, + "commercial_irrigated_square_feet": 13.04, + "intersection_density": 0.0000, + "gross_population_density": 0.012 + } + }, + { + "pk": 405, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 35.520, + "combined_pop_emp_density": 1.5969, + "residential_irrigated_square_feet": 109.87, + "pervious_hardscape_percent": 0.004, + "hardscape_percent": 0.259, + "impervious_roof_percent": 0.239, + "softscape_and_landscape_percent": 0.017, + "impervious_hardscape_percent": 0.020, + "parking_structure_square_feet": 9894.67, + "gross_net_ratio": 0.6443096, + "irrigated_percent": 0.282, + "household_density": 0.521, + "total_far": 0.7931751, + "floors": 1.776, + "residential_average_lot_size": 53059.50, + "commercial_irrigated_square_feet": 25.88, + "intersection_density": 0.0000, + "gross_population_density": 1.180 + } + }, + { + "pk": 406, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 55.551, + "combined_pop_emp_density": 11.2191, + "residential_irrigated_square_feet": 0.93, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.376, + "impervious_roof_percent": 0.367, + "softscape_and_landscape_percent": 0.004, + "impervious_hardscape_percent": 0.010, + "parking_structure_square_feet": 11610.83, + "gross_net_ratio": 0.5600000, + "irrigated_percent": 0.194, + "household_density": 0.029, + "total_far": 0.9246575, + "floors": 1.353, + "residential_average_lot_size": 2840.89, + "commercial_irrigated_square_feet": 16.78, + "intersection_density": 0.0000, + "gross_population_density": 0.028 + } + }, + { + "pk": 408, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 20.519, + "combined_pop_emp_density": 1.5350, + "residential_irrigated_square_feet": 64.14, + "pervious_hardscape_percent": 0.004, + "hardscape_percent": 0.117, + "impervious_roof_percent": 0.103, + "softscape_and_landscape_percent": 0.008, + "impervious_hardscape_percent": 0.014, + "parking_structure_square_feet": 3774.60, + "gross_net_ratio": 0.6220770, + "irrigated_percent": 0.341, + "household_density": 0.540, + "total_far": 0.5040917, + "floors": 1.468, + "residential_average_lot_size": 30397.64, + "commercial_irrigated_square_feet": 9.81, + "intersection_density": 0.0000, + "gross_population_density": 1.344 + } + }, + { + "pk": 410, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 14.256, + "combined_pop_emp_density": 1.5133, + "residential_irrigated_square_feet": 254.63, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.084, + "impervious_roof_percent": 0.060, + "softscape_and_landscape_percent": 0.025, + "impervious_hardscape_percent": 0.025, + "parking_structure_square_feet": 601.75, + "gross_net_ratio": 0.6379912, + "irrigated_percent": 0.411, + "household_density": 0.575, + "total_far": 0.3765974, + "floors": 1.356, + "residential_average_lot_size": 57312.39, + "commercial_irrigated_square_feet": 27.74, + "intersection_density": 0.0000, + "gross_population_density": 1.433 + } + }, + { + "pk": 411, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 7.683, + "combined_pop_emp_density": 1.4774, + "residential_irrigated_square_feet": 68.71, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.083, + "impervious_roof_percent": 0.068, + "softscape_and_landscape_percent": 0.010, + "impervious_hardscape_percent": 0.015, + "parking_structure_square_feet": 601.75, + "gross_net_ratio": 0.6379912, + "irrigated_percent": 0.281, + "household_density": 0.575, + "total_far": 0.2087304, + "floors": 1.379, + "residential_average_lot_size": 777212.02, + "commercial_irrigated_square_feet": 7.49, + "intersection_density": 0.0000, + "gross_population_density": 1.433 + } + }, + { + "pk": 413, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 35.245, + "combined_pop_emp_density": 8.1690, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.027, + "hardscape_percent": 0.535, + "impervious_roof_percent": 0.370, + "softscape_and_landscape_percent": 0.046, + "impervious_hardscape_percent": 0.165, + "parking_structure_square_feet": 1757.57, + "gross_net_ratio": 0.6988970, + "irrigated_percent": 0.285, + "household_density": 0.000, + "total_far": 0.4994245, + "floors": 1.596, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 396.72, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 415, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 17.734, + "combined_pop_emp_density": 3.2713, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.019, + "hardscape_percent": 0.705, + "impervious_roof_percent": 0.531, + "softscape_and_landscape_percent": 0.021, + "impervious_hardscape_percent": 0.173, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.7453567, + "irrigated_percent": 0.289, + "household_density": 0.000, + "total_far": 0.3186176, + "floors": 0.968, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 201.21, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 417, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 83.382, + "combined_pop_emp_density": 6.8083, + "residential_irrigated_square_feet": 9.10, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.352, + "impervious_roof_percent": 0.341, + "softscape_and_landscape_percent": 0.003, + "impervious_hardscape_percent": 0.011, + "parking_structure_square_feet": 25999.89, + "gross_net_ratio": 0.6925699, + "irrigated_percent": 0.154, + "household_density": 0.392, + "total_far": 1.5684979, + "floors": 3.432, + "residential_average_lot_size": 1467.85, + "commercial_irrigated_square_feet": 6.97, + "intersection_density": 0.0000, + "gross_population_density": 0.850 + } + }, + { + "pk": 418, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 38.269, + "combined_pop_emp_density": 9.4124, + "residential_irrigated_square_feet": 50.75, + "pervious_hardscape_percent": 0.023, + "hardscape_percent": 0.530, + "impervious_roof_percent": 0.371, + "softscape_and_landscape_percent": 0.045, + "impervious_hardscape_percent": 0.159, + "parking_structure_square_feet": 2607.96, + "gross_net_ratio": 0.6925699, + "irrigated_percent": 0.295, + "household_density": 0.088, + "total_far": 0.4501705, + "floors": 1.212, + "residential_average_lot_size": 1070.02, + "commercial_irrigated_square_feet": 349.52, + "intersection_density": 0.0000, + "gross_population_density": 0.187 + } + }, + { + "pk": 420, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 60.013, + "combined_pop_emp_density": 7.7329, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.027, + "hardscape_percent": 0.455, + "impervious_roof_percent": 0.266, + "softscape_and_landscape_percent": 0.053, + "impervious_hardscape_percent": 0.189, + "parking_structure_square_feet": 4413.14, + "gross_net_ratio": 0.7641394, + "irrigated_percent": 0.373, + "household_density": 0.000, + "total_far": 0.3830248, + "floors": 1.754, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 663.93, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 422, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 43.031, + "combined_pop_emp_density": 3.7744, + "residential_irrigated_square_feet": 14.78, + "pervious_hardscape_percent": 0.004, + "hardscape_percent": 0.411, + "impervious_roof_percent": 0.371, + "softscape_and_landscape_percent": 0.005, + "impervious_hardscape_percent": 0.040, + "parking_structure_square_feet": 8117.82, + "gross_net_ratio": 0.7641394, + "irrigated_percent": 0.207, + "household_density": 0.344, + "total_far": 0.6289631, + "floors": 1.960, + "residential_average_lot_size": 235.95, + "commercial_irrigated_square_feet": 18.06, + "intersection_density": 0.0000, + "gross_population_density": 0.860 + } + }, + { + "pk": 423, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 58.122, + "combined_pop_emp_density": 1.7024, + "residential_irrigated_square_feet": 8.63, + "pervious_hardscape_percent": 0.002, + "hardscape_percent": 0.378, + "impervious_roof_percent": 0.367, + "softscape_and_landscape_percent": 0.003, + "impervious_hardscape_percent": 0.010, + "parking_structure_square_feet": 15595.54, + "gross_net_ratio": 0.7094675, + "irrigated_percent": 0.097, + "household_density": 0.679, + "total_far": 0.6588908, + "floors": 2.063, + "residential_average_lot_size": 86.86, + "commercial_irrigated_square_feet": 0.39, + "intersection_density": 0.0000, + "gross_population_density": 1.697 + } + }, + { + "pk": 425, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 11.289, + "combined_pop_emp_density": 1.6350, + "residential_irrigated_square_feet": 283.56, + "pervious_hardscape_percent": 0.003, + "hardscape_percent": 0.154, + "impervious_roof_percent": 0.122, + "softscape_and_landscape_percent": 0.022, + "impervious_hardscape_percent": 0.032, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.7094675, + "irrigated_percent": 0.460, + "household_density": 0.649, + "total_far": 0.3250911, + "floors": 1.450, + "residential_average_lot_size": 204227.71, + "commercial_irrigated_square_feet": 26.20, + "intersection_density": 0.0000, + "gross_population_density": 1.624 + } + }, + { + "pk": 427, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 1.276, + "combined_pop_emp_density": 2.1241, + "residential_irrigated_square_feet": 0.29, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.001, + "impervious_roof_percent": 0.001, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.8508578, + "irrigated_percent": 0.065, + "household_density": 0.850, + "total_far": 0.0299787, + "floors": 1.701, + "residential_average_lot_size": 8190890.13, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 2.124 + } + }, + { + "pk": 428, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 0.372, + "combined_pop_emp_density": 1.8536, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.000, + "hardscape_percent": 0.000, + "impervious_roof_percent": 0.000, + "softscape_and_landscape_percent": 0.000, + "impervious_hardscape_percent": 0.000, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 0.9260494, + "irrigated_percent": 0.017, + "household_density": 0.742, + "total_far": 0.0077248, + "floors": 1.667, + "residential_average_lot_size": 8897860.18, + "commercial_irrigated_square_feet": 0.00, + "intersection_density": 0.0000, + "gross_population_density": 1.852 + } + }, + { + "pk": 430, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 74.134, + "combined_pop_emp_density": 13.5995, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.031, + "hardscape_percent": 0.555, + "impervious_roof_percent": 0.418, + "softscape_and_landscape_percent": 0.062, + "impervious_hardscape_percent": 0.137, + "parking_structure_square_feet": 16631.01, + "gross_net_ratio": 0.6889867, + "irrigated_percent": 0.593, + "household_density": 0.000, + "total_far": 0.6170000, + "floors": 2.002, + "residential_average_lot_size": 533.02, + "commercial_irrigated_square_feet": 1105.05, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 431, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 100.958, + "combined_pop_emp_density": 39.1683, + "residential_irrigated_square_feet": 31.79, + "pervious_hardscape_percent": 0.022, + "hardscape_percent": 0.443, + "impervious_roof_percent": 0.388, + "softscape_and_landscape_percent": 0.043, + "impervious_hardscape_percent": 0.055, + "parking_structure_square_feet": 30745.20, + "gross_net_ratio": 0.6247052, + "irrigated_percent": 0.437, + "household_density": 0.039, + "total_far": 1.5578942, + "floors": 4.269, + "residential_average_lot_size": 21412.07, + "commercial_irrigated_square_feet": 446.56, + "intersection_density": 0.0000, + "gross_population_density": 0.085 + } + }, + { + "pk": 401, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 402, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 403, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 404, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 405, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 406, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 407, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 408, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 409, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 410, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 411, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 412, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 413, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 414, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 415, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 416, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 417, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 418, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 419, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 420, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 421, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 422, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 423, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 424, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 425, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 426, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 427, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 428, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 429, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 430, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 431, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 432, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 433, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 434, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 435, + "model": "main.infrastructureattributeset", + "fields": {} + }, + { + "pk": 1, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 6, + "name": "Park", + "tags": [], + "description": null + } + }, + { + "pk": 2, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 7, + "name": "Streets", + "tags": [], + "description": null + } + }, + { + "pk": 3, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 8, + "name": "Detention/Utilities", + "tags": [], + "description": null + } + }, + { + "pk": 344, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 247, + "name": "High-Rise Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 345, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 248, + "name": "Non-Urban Middle School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 346, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 249, + "name": "Campus/College Low", + "tags": [ + 3 + ], + "description": null + } + }, + { + "pk": 347, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 250, + "name": "Industrial Low", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 348, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 251, + "name": "Non-Urban High School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 349, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 252, + "name": "Urban Mid-Rise Residential", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 350, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 253, + "name": "Urban Middle School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 351, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 254, + "name": "High-Rise Office", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 352, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 255, + "name": "Estate Lot", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 353, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 256, + "name": "Medium Lot 5500", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 354, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 257, + "name": "Small Lot 4000", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 355, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 258, + "name": "Skyscraper Residential", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 356, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 259, + "name": "Urban High School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 357, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 260, + "name": "Mid-Rise Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 358, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 261, + "name": "Hotel High", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 359, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 262, + "name": "Parking Structure/Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 360, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 263, + "name": "Low-Rise Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 361, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 264, + "name": "Low-Rise Office", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 362, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 265, + "name": "Skyscraper Office", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 363, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 266, + "name": "Mid-Rise Office", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 364, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 267, + "name": "Warehouse High", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 369, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 271, + "name": "Rural Ranchette", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 370, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 272, + "name": "Parking Structure", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 371, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 273, + "name": "Urban Podium Multi-Family", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 373, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 275, + "name": "Garden Apartment", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 376, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 278, + "name": "Very Small Lot 3000", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 377, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 279, + "name": "Office Park Low", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 378, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 280, + "name": "Hotel Low", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 343, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 246, + "name": "Main Street Commercial/MU Low (1-2 Floors)", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 342, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 361, + "name": "Low Intensity Strip Commercial (weighted avg)", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 379, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 423, + "name": "Urban Townhome/Live-Work", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 380, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 424, + "name": "High-Rise Residential", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 381, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 425, + "name": "Suburban Multifamily Apt/Condo", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 382, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 426, + "name": "Rural Residential", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 383, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 427, + "name": "Urban Elementary School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 384, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 428, + "name": "Skyscraper Mixed Use", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 385, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 429, + "name": "Industrial High", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 387, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 431, + "name": "Non-Urban Elementary School", + "tags": [ + 2 + ], + "description": null + } + }, + { + "pk": 388, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 432, + "name": "Standard Townhome", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 389, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 433, + "name": "Rural Employment", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 390, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 434, + "name": "Office Park High", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 391, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 435, + "name": "Regional Mall", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 392, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 436, + "name": "Main Street Commercial/MU High (3-5 Floors)", + "tags": [ + 1 + ], + "description": null + } + }, + { + "pk": 395, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 439, + "name": "Campus/College High", + "tags": [ + 3 + ], + "description": null + } + }, + { + "pk": 397, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 441, + "name": "Parking Structure+Ground-Floor Retail", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 398, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 442, + "name": "Warehouse Low", + "tags": [ + 4 + ], + "description": null + } + }, + { + "pk": 399, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 443, + "name": "Large Lot 7500", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 400, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 444, + "name": "Hospital/Civic/Other Institutional", + "tags": [ + 3 + ], + "description": null + } + }, + { + "pk": 366, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 268, + "name": "Standard Podium Multi-Family", + "tags": [ + 5 + ], + "description": null + } + }, + { + "pk": 365, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 422, + "name": "Medium Intensity Strip Commercial (weighted avg)", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 396, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 440, + "name": "Main Street Commercial (Retail + Office/Medical)", + "tags": [ + 6 + ], + "description": null + } + }, + { + "pk": 415, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 363, + "name": "Office Focus", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 416, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 364, + "name": "Mixed Office and R&D", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 417, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 365, + "name": "Office/Industrial", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 419, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 367, + "name": "Low-Density Employment Park", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 420, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 368, + "name": "High Intensity Activity Center", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 421, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 369, + "name": "Mid Intensity Activity Center", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 422, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 370, + "name": "Low Intensity Retail-Centered N'Hood", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 423, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 371, + "name": "Retail: Strip Mall/ Big Box", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 424, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 372, + "name": "Industrial/Office/Res Mixed High", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 425, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 373, + "name": "Industrial/Office/Res Mixed Low", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 426, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 374, + "name": "Suburban Multifamily", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 427, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 375, + "name": "Suburban Mixed Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 428, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 376, + "name": "Residential Subdivision", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 429, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 377, + "name": "Large Lot Residential Area", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 430, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 378, + "name": "Rural Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 432, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 380, + "name": "Rural Employment", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 433, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 381, + "name": "Campus/ University", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 434, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 382, + "name": "Institutional", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 401, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 445, + "name": "Urban Mixed Use", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 402, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 446, + "name": "Urban Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 403, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 447, + "name": "Urban Commercial", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 404, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 448, + "name": "City Mixed Use", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 405, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 449, + "name": "City Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 406, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 450, + "name": "City Commercial", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 407, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 451, + "name": "Town Mixed Use", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 408, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 452, + "name": "Town Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 409, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 453, + "name": "Town Commercial", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 410, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 454, + "name": "Village Mixed Use", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 411, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 455, + "name": "Village Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 412, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 456, + "name": "Village Commercial", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 413, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 457, + "name": "Neighborhood Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 414, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 362, + "name": "Neighborhood Low", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 435, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 404, + "name": "Parks & Open Space", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 418, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 366, + "name": "Industrial Focus", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 431, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 379, + "name": "Rural Ranchettes", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 1, + "model": "main.placetypecomponent", + "fields": { + "tags": [] + } + }, + { + "pk": 2, + "model": "main.placetypecomponent", + "fields": { + "tags": [] + } + }, + { + "pk": 3, + "model": "main.placetypecomponent", + "fields": { + "tags": [] + } + }, + { + "pk": 344, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 345, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 346, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 3 + ] + } + }, + { + "pk": 347, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 348, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 349, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 350, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 351, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 352, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 353, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 354, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 355, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 356, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 357, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 358, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 359, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 360, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 361, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 362, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 363, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 364, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 369, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 370, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 371, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 373, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 376, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 377, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 378, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 343, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 342, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 379, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 380, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 381, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 382, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 383, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 384, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 385, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 387, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 2 + ] + } + }, + { + "pk": 388, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 389, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 390, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 391, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 392, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 1 + ] + } + }, + { + "pk": 395, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 3 + ] + } + }, + { + "pk": 397, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 398, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 4 + ] + } + }, + { + "pk": 399, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 400, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 3 + ] + } + }, + { + "pk": 366, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 5 + ] + } + }, + { + "pk": 365, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 396, + "model": "main.placetypecomponent", + "fields": { + "tags": [ + 6 + ] + } + }, + { + "pk": 401, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 401, + "building_attributes": 398 + } + }, + { + "pk": 402, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 402, + "building_attributes": 399 + } + }, + { + "pk": 403, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 403, + "building_attributes": 400 + } + }, + { + "pk": 404, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 404, + "building_attributes": 401 + } + }, + { + "pk": 405, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 405, + "building_attributes": 402 + } + }, + { + "pk": 406, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 406, + "building_attributes": 403 + } + }, + { + "pk": 407, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 407, + "building_attributes": 404 + } + }, + { + "pk": 408, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 408, + "building_attributes": 405 + } + }, + { + "pk": 409, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 409, + "building_attributes": 406 + } + }, + { + "pk": 410, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 410, + "building_attributes": 407 + } + }, + { + "pk": 411, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 411, + "building_attributes": 408 + } + }, + { + "pk": 412, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 412, + "building_attributes": 409 + } + }, + { + "pk": 413, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 413, + "building_attributes": 410 + } + }, + { + "pk": 414, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 414, + "building_attributes": 411 + } + }, + { + "pk": 415, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 415, + "building_attributes": 412 + } + }, + { + "pk": 416, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 416, + "building_attributes": 413 + } + }, + { + "pk": 417, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 417, + "building_attributes": 414 + } + }, + { + "pk": 418, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 418, + "building_attributes": 415 + } + }, + { + "pk": 419, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 419, + "building_attributes": 416 + } + }, + { + "pk": 420, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 420, + "building_attributes": 417 + } + }, + { + "pk": 421, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 421, + "building_attributes": 418 + } + }, + { + "pk": 422, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 422, + "building_attributes": 419 + } + }, + { + "pk": 423, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 423, + "building_attributes": 420 + } + }, + { + "pk": 424, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 424, + "building_attributes": 421 + } + }, + { + "pk": 425, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 425, + "building_attributes": 422 + } + }, + { + "pk": 426, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 426, + "building_attributes": 423 + } + }, + { + "pk": 427, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 427, + "building_attributes": 424 + } + }, + { + "pk": 428, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 428, + "building_attributes": 425 + } + }, + { + "pk": 429, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 429, + "building_attributes": 426 + } + }, + { + "pk": 430, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 430, + "building_attributes": 427 + } + }, + { + "pk": 431, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 431, + "building_attributes": 428 + } + }, + { + "pk": 432, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 432, + "building_attributes": 429 + } + }, + { + "pk": 433, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 433, + "building_attributes": 430 + } + }, + { + "pk": 434, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 434, + "building_attributes": 431 + } + }, + { + "pk": 435, + "model": "main.placetype", + "fields": { + "tags": [ + 8 + ], + "infrastructureattributeset_ptr": 435, + "building_attributes": 432 + } + }, + { + "pk": 1, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 383, + "percent": 0.0100000 + } + }, + { + "pk": 2, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 357, + "percent": 0.0102730 + } + }, + { + "pk": 3, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 380, + "percent": 0.1695037 + } + }, + { + "pk": 4, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 344, + "percent": 0.0513648 + } + }, + { + "pk": 5, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 355, + "percent": 0.1181390 + } + }, + { + "pk": 6, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 7, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 350, + "percent": 0.0100000 + } + }, + { + "pk": 8, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 384, + "percent": 0.0513648 + } + }, + { + "pk": 9, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 358, + "percent": 0.0102730 + } + }, + { + "pk": 10, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 392, + "percent": 0.0154094 + } + }, + { + "pk": 11, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 349, + "percent": 0.0102730 + } + }, + { + "pk": 12, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 13, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 351, + "percent": 0.0308189 + } + }, + { + "pk": 14, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 362, + "percent": 0.0308189 + } + }, + { + "pk": 15, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 363, + "percent": 0.0154094 + } + }, + { + "pk": 16, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 17, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 2, + "percent": 0.3663523 + } + }, + { + "pk": 18, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 401, + "placetype_component": 1, + "percent": 0.0700000 + } + }, + { + "pk": 19, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 383, + "percent": 0.0200000 + } + }, + { + "pk": 20, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 384, + "percent": 0.0051365 + } + }, + { + "pk": 21, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 380, + "percent": 0.1540943 + } + }, + { + "pk": 22, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 371, + "percent": 0.0513648 + } + }, + { + "pk": 23, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 344, + "percent": 0.0051365 + } + }, + { + "pk": 24, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 355, + "percent": 0.0513648 + } + }, + { + "pk": 25, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 26, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 350, + "percent": 0.0100000 + } + }, + { + "pk": 27, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 358, + "percent": 0.0256824 + } + }, + { + "pk": 28, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 392, + "percent": 0.0256824 + } + }, + { + "pk": 29, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 349, + "percent": 0.1438214 + } + }, + { + "pk": 30, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 379, + "percent": 0.0513648 + } + }, + { + "pk": 31, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 32, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 2, + "percent": 0.3663523 + } + }, + { + "pk": 33, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 402, + "placetype_component": 1, + "percent": 0.0700000 + } + }, + { + "pk": 34, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 360, + "percent": 0.0054365 + } + }, + { + "pk": 35, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 370, + "percent": 0.0163094 + } + }, + { + "pk": 36, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 344, + "percent": 0.0108730 + } + }, + { + "pk": 37, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 357, + "percent": 0.0054365 + } + }, + { + "pk": 38, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 384, + "percent": 0.0163094 + } + }, + { + "pk": 39, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 358, + "percent": 0.0271824 + } + }, + { + "pk": 40, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 392, + "percent": 0.0271824 + } + }, + { + "pk": 41, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 42, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 351, + "percent": 0.0543648 + } + }, + { + "pk": 43, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 361, + "percent": 0.1630943 + } + }, + { + "pk": 44, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 362, + "percent": 0.0271824 + } + }, + { + "pk": 45, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 396, + "percent": 0.0543648 + } + }, + { + "pk": 46, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 397, + "percent": 0.0271824 + } + }, + { + "pk": 47, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 363, + "percent": 0.1087295 + } + }, + { + "pk": 48, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 49, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 2, + "percent": 0.3663523 + } + }, + { + "pk": 50, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 403, + "placetype_component": 1, + "percent": 0.0700000 + } + }, + { + "pk": 51, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 383, + "percent": 0.0200000 + } + }, + { + "pk": 52, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 357, + "percent": 0.0510000 + } + }, + { + "pk": 53, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 359, + "percent": 0.0153000 + } + }, + { + "pk": 54, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 343, + "percent": 0.0051000 + } + }, + { + "pk": 55, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 56, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 350, + "percent": 0.0100000 + } + }, + { + "pk": 57, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 358, + "percent": 0.0153000 + } + }, + { + "pk": 58, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 371, + "percent": 0.0255000 + } + }, + { + "pk": 59, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 392, + "percent": 0.0510000 + } + }, + { + "pk": 60, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 349, + "percent": 0.0204000 + } + }, + { + "pk": 61, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 62, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 360, + "percent": 0.0561000 + } + }, + { + "pk": 63, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 361, + "percent": 0.0765000 + } + }, + { + "pk": 64, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 379, + "percent": 0.0510000 + } + }, + { + "pk": 65, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 396, + "percent": 0.0255000 + } + }, + { + "pk": 66, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 397, + "percent": 0.0153000 + } + }, + { + "pk": 67, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 366, + "percent": 0.0255000 + } + }, + { + "pk": 68, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 363, + "percent": 0.0765000 + } + }, + { + "pk": 69, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 70, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 2, + "percent": 0.3600000 + } + }, + { + "pk": 71, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 404, + "placetype_component": 1, + "percent": 0.0700000 + } + }, + { + "pk": 72, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 383, + "percent": 0.0200000 + } + }, + { + "pk": 73, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 343, + "percent": 0.0255000 + } + }, + { + "pk": 74, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 75, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 350, + "percent": 0.0100000 + } + }, + { + "pk": 76, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 358, + "percent": 0.0204000 + } + }, + { + "pk": 77, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 349, + "percent": 0.0663000 + } + }, + { + "pk": 78, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 79, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 371, + "percent": 0.0765000 + } + }, + { + "pk": 80, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 379, + "percent": 0.1836000 + } + }, + { + "pk": 81, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 366, + "percent": 0.1326000 + } + }, + { + "pk": 82, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 359, + "percent": 0.0051000 + } + }, + { + "pk": 83, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 84, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 2, + "percent": 0.3600000 + } + }, + { + "pk": 85, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 405, + "placetype_component": 1, + "percent": 0.0700000 + } + }, + { + "pk": 86, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 343, + "percent": 0.0275000 + } + }, + { + "pk": 87, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 358, + "percent": 0.0165000 + } + }, + { + "pk": 88, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 89, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 370, + "percent": 0.0275000 + } + }, + { + "pk": 90, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 361, + "percent": 0.1595000 + } + }, + { + "pk": 91, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 396, + "percent": 0.2145000 + } + }, + { + "pk": 92, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 397, + "percent": 0.0605000 + } + }, + { + "pk": 93, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 363, + "percent": 0.0440000 + } + }, + { + "pk": 94, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 95, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 2, + "percent": 0.3600000 + } + }, + { + "pk": 96, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 406, + "placetype_component": 1, + "percent": 0.0700000 + } + }, + { + "pk": 97, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 383, + "percent": 0.0400000 + } + }, + { + "pk": 98, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 359, + "percent": 0.0240000 + } + }, + { + "pk": 99, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 343, + "percent": 0.0720000 + } + }, + { + "pk": 100, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 101, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 350, + "percent": 0.0200000 + } + }, + { + "pk": 102, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 371, + "percent": 0.0240000 + } + }, + { + "pk": 103, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 392, + "percent": 0.0624000 + } + }, + { + "pk": 104, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 378, + "percent": 0.0144000 + } + }, + { + "pk": 105, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 360, + "percent": 0.0240000 + } + }, + { + "pk": 106, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 361, + "percent": 0.0672000 + } + }, + { + "pk": 107, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 379, + "percent": 0.0960000 + } + }, + { + "pk": 108, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 396, + "percent": 0.0960000 + } + }, + { + "pk": 109, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 110, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 111, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 2, + "percent": 0.3600000 + } + }, + { + "pk": 112, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 407, + "placetype_component": 1, + "percent": 0.0700000 + } + }, + { + "pk": 113, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 383, + "percent": 0.0400000 + } + }, + { + "pk": 114, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 353, + "percent": 0.1128619 + } + }, + { + "pk": 115, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 343, + "percent": 0.0282155 + } + }, + { + "pk": 116, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 376, + "percent": 0.0282155 + } + }, + { + "pk": 117, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 354, + "percent": 0.1072188 + } + }, + { + "pk": 118, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 119, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 350, + "percent": 0.0200000 + } + }, + { + "pk": 120, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 392, + "percent": 0.1185050 + } + }, + { + "pk": 121, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 122, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 371, + "percent": 0.0282155 + } + }, + { + "pk": 123, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 379, + "percent": 0.1410774 + } + }, + { + "pk": 124, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 125, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 2, + "percent": 0.2756904 + } + }, + { + "pk": 126, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 408, + "placetype_component": 1, + "percent": 0.0700000 + } + }, + { + "pk": 127, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 343, + "percent": 0.0275000 + } + }, + { + "pk": 128, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 392, + "percent": 0.0165000 + } + }, + { + "pk": 129, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 378, + "percent": 0.0165000 + } + }, + { + "pk": 130, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 370, + "percent": 0.0275000 + } + }, + { + "pk": 131, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 361, + "percent": 0.0275000 + } + }, + { + "pk": 132, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 396, + "percent": 0.3960000 + } + }, + { + "pk": 133, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 397, + "percent": 0.0275000 + } + }, + { + "pk": 134, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 365, + "percent": 0.0110000 + } + }, + { + "pk": 135, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 136, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 137, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 2, + "percent": 0.3600000 + } + }, + { + "pk": 138, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 409, + "placetype_component": 1, + "percent": 0.0700000 + } + }, + { + "pk": 139, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 383, + "percent": 0.0400000 + } + }, + { + "pk": 140, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 343, + "percent": 0.0784320 + } + }, + { + "pk": 141, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 376, + "percent": 0.0490200 + } + }, + { + "pk": 142, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 354, + "percent": 0.0490200 + } + }, + { + "pk": 143, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 144, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 350, + "percent": 0.0200000 + } + }, + { + "pk": 145, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 392, + "percent": 0.0098040 + } + }, + { + "pk": 146, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 378, + "percent": 0.0245100 + } + }, + { + "pk": 147, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 379, + "percent": 0.0980400 + } + }, + { + "pk": 148, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 396, + "percent": 0.1470599 + } + }, + { + "pk": 149, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 365, + "percent": 0.0343140 + } + }, + { + "pk": 150, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 151, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 152, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 2, + "percent": 0.3198002 + } + }, + { + "pk": 153, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 410, + "placetype_component": 1, + "percent": 0.1000000 + } + }, + { + "pk": 154, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 383, + "percent": 0.0400000 + } + }, + { + "pk": 155, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 343, + "percent": 0.0054208 + } + }, + { + "pk": 156, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 354, + "percent": 0.1409400 + } + }, + { + "pk": 157, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 158, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 376, + "percent": 0.1897269 + } + }, + { + "pk": 159, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 350, + "percent": 0.0200000 + } + }, + { + "pk": 160, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 379, + "percent": 0.2059893 + } + }, + { + "pk": 161, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 162, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 163, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 2, + "percent": 0.2679230 + } + }, + { + "pk": 164, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 411, + "placetype_component": 1, + "percent": 0.1000000 + } + }, + { + "pk": 165, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 412, + "placetype_component": 342, + "percent": 0.1476719 + } + }, + { + "pk": 166, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 412, + "placetype_component": 343, + "percent": 0.0369180 + } + }, + { + "pk": 167, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 412, + "placetype_component": 378, + "percent": 0.0205100 + } + }, + { + "pk": 168, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 412, + "placetype_component": 396, + "percent": 0.1640799 + } + }, + { + "pk": 169, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 412, + "placetype_component": 365, + "percent": 0.0410200 + } + }, + { + "pk": 170, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 412, + "placetype_component": 400, + "percent": 0.0200000 + } + }, + { + "pk": 171, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 412, + "placetype_component": 3, + "percent": 0.0500000 + } + }, + { + "pk": 172, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 412, + "placetype_component": 2, + "percent": 0.3198002 + } + }, + { + "pk": 173, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 412, + "placetype_component": 1, + "percent": 0.2000000 + } + }, + { + "pk": 174, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 383, + "percent": 0.0200000 + } + }, + { + "pk": 175, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 353, + "percent": 0.2311965 + } + }, + { + "pk": 176, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 343, + "percent": 0.0057799 + } + }, + { + "pk": 177, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 376, + "percent": 0.1387179 + } + }, + { + "pk": 178, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 354, + "percent": 0.2022969 + } + }, + { + "pk": 179, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 180, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 350, + "percent": 0.0200000 + } + }, + { + "pk": 181, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 182, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 183, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 2, + "percent": 0.2520088 + } + }, + { + "pk": 184, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 413, + "placetype_component": 1, + "percent": 0.1000000 + } + }, + { + "pk": 185, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 383, + "percent": 0.0200000 + } + }, + { + "pk": 186, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 353, + "percent": 0.0462393 + } + }, + { + "pk": 187, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 343, + "percent": 0.0057799 + } + }, + { + "pk": 188, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 376, + "percent": 0.0288996 + } + }, + { + "pk": 189, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 354, + "percent": 0.0346795 + } + }, + { + "pk": 190, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 356, + "percent": 0.0100000 + } + }, + { + "pk": 191, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 350, + "percent": 0.0200000 + } + }, + { + "pk": 192, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 400, + "percent": 0.0100000 + } + }, + { + "pk": 193, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 352, + "percent": 0.1444978 + } + }, + { + "pk": 194, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 399, + "percent": 0.0577991 + } + }, + { + "pk": 195, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 382, + "percent": 0.2600961 + } + }, + { + "pk": 196, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 3, + "percent": 0.0100000 + } + }, + { + "pk": 197, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 2, + "percent": 0.2520088 + } + }, + { + "pk": 198, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 414, + "placetype_component": 1, + "percent": 0.1000000 + } + }, + { + "pk": 199, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 415, + "placetype_component": 398, + "percent": 0.0194669 + } + }, + { + "pk": 200, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 415, + "placetype_component": 390, + "percent": 0.4996507 + } + }, + { + "pk": 201, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 415, + "placetype_component": 377, + "percent": 0.0324449 + } + }, + { + "pk": 202, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 415, + "placetype_component": 400, + "percent": 0.0500000 + } + }, + { + "pk": 203, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 415, + "placetype_component": 364, + "percent": 0.0648897 + } + }, + { + "pk": 204, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 415, + "placetype_component": 365, + "percent": 0.0324449 + } + }, + { + "pk": 205, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 415, + "placetype_component": 3, + "percent": 0.0500000 + } + }, + { + "pk": 206, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 415, + "placetype_component": 2, + "percent": 0.2111030 + } + }, + { + "pk": 207, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 415, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 208, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 342, + "percent": 0.0069890 + } + }, + { + "pk": 209, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 398, + "percent": 0.0978456 + } + }, + { + "pk": 210, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 390, + "percent": 0.0908566 + } + }, + { + "pk": 211, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 358, + "percent": 0.0139779 + } + }, + { + "pk": 212, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 377, + "percent": 0.3634265 + } + }, + { + "pk": 213, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 378, + "percent": 0.0209669 + } + }, + { + "pk": 214, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 364, + "percent": 0.0768787 + } + }, + { + "pk": 215, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 365, + "percent": 0.0279559 + } + }, + { + "pk": 216, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 3, + "percent": 0.0500000 + } + }, + { + "pk": 217, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 2, + "percent": 0.2111030 + } + }, + { + "pk": 218, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 416, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 219, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 342, + "percent": 0.0529512 + } + }, + { + "pk": 220, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 385, + "percent": 0.0453868 + } + }, + { + "pk": 221, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 347, + "percent": 0.1059025 + } + }, + { + "pk": 222, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 398, + "percent": 0.2950141 + } + }, + { + "pk": 223, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 390, + "percent": 0.0378223 + } + }, + { + "pk": 224, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 377, + "percent": 0.1210314 + } + }, + { + "pk": 225, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 364, + "percent": 0.0756446 + } + }, + { + "pk": 226, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 365, + "percent": 0.0226934 + } + }, + { + "pk": 227, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 3, + "percent": 0.0300000 + } + }, + { + "pk": 228, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 2, + "percent": 0.1735537 + } + }, + { + "pk": 229, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 417, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 230, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 418, + "placetype_component": 342, + "percent": 0.1490713 + } + }, + { + "pk": 231, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 418, + "placetype_component": 385, + "percent": 0.1341642 + } + }, + { + "pk": 232, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 418, + "placetype_component": 377, + "percent": 0.0149071 + } + }, + { + "pk": 233, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 418, + "placetype_component": 398, + "percent": 0.2012463 + } + }, + { + "pk": 234, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 418, + "placetype_component": 347, + "percent": 0.2086999 + } + }, + { + "pk": 235, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 418, + "placetype_component": 364, + "percent": 0.0372678 + } + }, + { + "pk": 236, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 418, + "placetype_component": 3, + "percent": 0.0500000 + } + }, + { + "pk": 237, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 418, + "placetype_component": 2, + "percent": 0.1646433 + } + }, + { + "pk": 238, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 418, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 239, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 419, + "placetype_component": 347, + "percent": 0.3248000 + } + }, + { + "pk": 240, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 419, + "placetype_component": 389, + "percent": 0.1400000 + } + }, + { + "pk": 241, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 419, + "placetype_component": 398, + "percent": 0.0952000 + } + }, + { + "pk": 242, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 419, + "placetype_component": 3, + "percent": 0.0500000 + } + }, + { + "pk": 243, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 419, + "placetype_component": 2, + "percent": 0.3500000 + } + }, + { + "pk": 244, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 419, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 245, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 381, + "percent": 0.0692570 + } + }, + { + "pk": 246, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 388, + "percent": 0.1731425 + } + }, + { + "pk": 247, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 358, + "percent": 0.0346285 + } + }, + { + "pk": 248, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 392, + "percent": 0.1038855 + } + }, + { + "pk": 249, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 360, + "percent": 0.0346285 + } + }, + { + "pk": 250, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 361, + "percent": 0.1108112 + } + }, + { + "pk": 251, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 396, + "percent": 0.0969598 + } + }, + { + "pk": 252, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 397, + "percent": 0.0346285 + } + }, + { + "pk": 253, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 366, + "percent": 0.0346285 + } + }, + { + "pk": 254, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 255, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 2, + "percent": 0.2474301 + } + }, + { + "pk": 256, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 420, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 257, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 342, + "percent": 0.2077710 + } + }, + { + "pk": 258, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 381, + "percent": 0.0346285 + } + }, + { + "pk": 259, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 343, + "percent": 0.0346285 + } + }, + { + "pk": 260, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 373, + "percent": 0.0207771 + } + }, + { + "pk": 261, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 391, + "percent": 0.1038855 + } + }, + { + "pk": 262, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 378, + "percent": 0.0346285 + } + }, + { + "pk": 263, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 388, + "percent": 0.0138514 + } + }, + { + "pk": 264, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 396, + "percent": 0.1385140 + } + }, + { + "pk": 265, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 365, + "percent": 0.1038855 + } + }, + { + "pk": 266, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 267, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 2, + "percent": 0.2474301 + } + }, + { + "pk": 268, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 421, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 269, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 342, + "percent": 0.1990273 + } + }, + { + "pk": 270, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 381, + "percent": 0.0124392 + } + }, + { + "pk": 271, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 353, + "percent": 0.1057333 + } + }, + { + "pk": 272, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 387, + "percent": 0.0600000 + } + }, + { + "pk": 273, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 373, + "percent": 0.0186588 + } + }, + { + "pk": 274, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 354, + "percent": 0.0684156 + } + }, + { + "pk": 275, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 345, + "percent": 0.0400000 + } + }, + { + "pk": 276, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 399, + "percent": 0.1243921 + } + }, + { + "pk": 277, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 348, + "percent": 0.0200000 + } + }, + { + "pk": 278, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 378, + "percent": 0.0124392 + } + }, + { + "pk": 279, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 365, + "percent": 0.0497568 + } + }, + { + "pk": 280, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 352, + "percent": 0.0310980 + } + }, + { + "pk": 281, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 282, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 2, + "percent": 0.1980397 + } + }, + { + "pk": 283, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 422, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 284, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 423, + "placetype_component": 342, + "percent": 0.1910348 + } + }, + { + "pk": 285, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 423, + "placetype_component": 390, + "percent": 0.2292418 + } + }, + { + "pk": 286, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 423, + "placetype_component": 365, + "percent": 0.2292418 + } + }, + { + "pk": 287, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 423, + "placetype_component": 391, + "percent": 0.1146209 + } + }, + { + "pk": 288, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 423, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 289, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 423, + "placetype_component": 2, + "percent": 0.1758606 + } + }, + { + "pk": 290, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 423, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 291, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 381, + "percent": 0.0764139 + } + }, + { + "pk": 292, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 385, + "percent": 0.0305656 + } + }, + { + "pk": 293, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 347, + "percent": 0.0458484 + } + }, + { + "pk": 294, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 373, + "percent": 0.0382070 + } + }, + { + "pk": 295, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 398, + "percent": 0.0229242 + } + }, + { + "pk": 296, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 390, + "percent": 0.0764139 + } + }, + { + "pk": 297, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 377, + "percent": 0.0764139 + } + }, + { + "pk": 298, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 388, + "percent": 0.0382070 + } + }, + { + "pk": 299, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 364, + "percent": 0.0152828 + } + }, + { + "pk": 300, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 366, + "percent": 0.3438627 + } + }, + { + "pk": 301, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 302, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 2, + "percent": 0.1758606 + } + }, + { + "pk": 303, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 424, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 304, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 342, + "percent": 0.0382070 + } + }, + { + "pk": 305, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 381, + "percent": 0.1757521 + } + }, + { + "pk": 306, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 385, + "percent": 0.1528279 + } + }, + { + "pk": 307, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 373, + "percent": 0.0764139 + } + }, + { + "pk": 308, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 398, + "percent": 0.0382070 + } + }, + { + "pk": 309, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 390, + "percent": 0.1146209 + } + }, + { + "pk": 310, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 388, + "percent": 0.0916967 + } + }, + { + "pk": 311, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 364, + "percent": 0.0764139 + } + }, + { + "pk": 312, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 313, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 2, + "percent": 0.1758606 + } + }, + { + "pk": 314, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 425, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 315, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 381, + "percent": 0.6115208 + } + }, + { + "pk": 316, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 387, + "percent": 0.0100000 + } + }, + { + "pk": 317, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 373, + "percent": 0.0271787 + } + }, + { + "pk": 318, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 345, + "percent": 0.0100000 + } + }, + { + "pk": 319, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 348, + "percent": 0.0100000 + } + }, + { + "pk": 320, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 388, + "percent": 0.0339734 + } + }, + { + "pk": 321, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 366, + "percent": 0.0067947 + } + }, + { + "pk": 322, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 323, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 2, + "percent": 0.2305325 + } + }, + { + "pk": 324, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 426, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 325, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 381, + "percent": 0.1298935 + } + }, + { + "pk": 326, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 353, + "percent": 0.0974201 + } + }, + { + "pk": 327, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 387, + "percent": 0.0200000 + } + }, + { + "pk": 328, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 373, + "percent": 0.0974201 + } + }, + { + "pk": 329, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 354, + "percent": 0.1428829 + } + }, + { + "pk": 330, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 345, + "percent": 0.0200000 + } + }, + { + "pk": 331, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 376, + "percent": 0.0324734 + } + }, + { + "pk": 332, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 348, + "percent": 0.0200000 + } + }, + { + "pk": 333, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 388, + "percent": 0.0649468 + } + }, + { + "pk": 334, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 399, + "percent": 0.0844308 + } + }, + { + "pk": 335, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 336, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 2, + "percent": 0.2305325 + } + }, + { + "pk": 337, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 427, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 338, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 353, + "percent": 0.1948403 + } + }, + { + "pk": 339, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 387, + "percent": 0.0200000 + } + }, + { + "pk": 340, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 354, + "percent": 0.1298935 + } + }, + { + "pk": 341, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 345, + "percent": 0.0200000 + } + }, + { + "pk": 342, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 348, + "percent": 0.0200000 + } + }, + { + "pk": 343, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 399, + "percent": 0.2273136 + } + }, + { + "pk": 344, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 352, + "percent": 0.0974201 + } + }, + { + "pk": 345, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 346, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 2, + "percent": 0.2305325 + } + }, + { + "pk": 347, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 428, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 348, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 429, + "placetype_component": 387, + "percent": 0.0200000 + } + }, + { + "pk": 349, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 429, + "placetype_component": 345, + "percent": 0.0100000 + } + }, + { + "pk": 350, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 429, + "placetype_component": 348, + "percent": 0.0100000 + } + }, + { + "pk": 351, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 429, + "placetype_component": 352, + "percent": 0.1839072 + } + }, + { + "pk": 352, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 429, + "placetype_component": 399, + "percent": 0.1103443 + } + }, + { + "pk": 353, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 429, + "placetype_component": 382, + "percent": 0.4413774 + } + }, + { + "pk": 354, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 429, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 355, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 429, + "placetype_component": 2, + "percent": 0.1643711 + } + }, + { + "pk": 356, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 429, + "placetype_component": 1, + "percent": 0.0400000 + } + }, + { + "pk": 357, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 430, + "placetype_component": 369, + "percent": 0.4673118 + } + }, + { + "pk": 358, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 430, + "placetype_component": 387, + "percent": 0.0002000 + } + }, + { + "pk": 359, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 430, + "placetype_component": 345, + "percent": 0.0005000 + } + }, + { + "pk": 360, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 430, + "placetype_component": 348, + "percent": 0.0005000 + } + }, + { + "pk": 361, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 430, + "placetype_component": 352, + "percent": 0.0424829 + } + }, + { + "pk": 362, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 430, + "placetype_component": 382, + "percent": 0.3398631 + } + }, + { + "pk": 363, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 430, + "placetype_component": 3, + "percent": 0.0400000 + } + }, + { + "pk": 364, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 430, + "placetype_component": 2, + "percent": 0.0991422 + } + }, + { + "pk": 365, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 430, + "placetype_component": 1, + "percent": 0.0100000 + } + }, + { + "pk": 366, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 431, + "placetype_component": 389, + "percent": 0.1849321 + } + }, + { + "pk": 367, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 431, + "placetype_component": 369, + "percent": 0.6482346 + } + }, + { + "pk": 368, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 431, + "placetype_component": 398, + "percent": 0.0002778 + } + }, + { + "pk": 369, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 431, + "placetype_component": 382, + "percent": 0.0926049 + } + }, + { + "pk": 370, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 431, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 371, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 431, + "placetype_component": 2, + "percent": 0.0439506 + } + }, + { + "pk": 372, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 431, + "placetype_component": 1, + "percent": 0.0100000 + } + }, + { + "pk": 373, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 432, + "placetype_component": 342, + "percent": 0.0459366 + } + }, + { + "pk": 374, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 432, + "placetype_component": 389, + "percent": 0.8165467 + } + }, + { + "pk": 375, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 432, + "placetype_component": 369, + "percent": 0.0749985 + } + }, + { + "pk": 376, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 432, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 377, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 432, + "placetype_component": 2, + "percent": 0.0325181 + } + }, + { + "pk": 378, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 432, + "placetype_component": 1, + "percent": 0.0100000 + } + }, + { + "pk": 379, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 433, + "placetype_component": 395, + "percent": 0.3000000 + } + }, + { + "pk": 380, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 433, + "placetype_component": 346, + "percent": 0.3000000 + } + }, + { + "pk": 381, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 433, + "placetype_component": 370, + "percent": 0.0700000 + } + }, + { + "pk": 382, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 433, + "placetype_component": 400, + "percent": 0.0200000 + } + }, + { + "pk": 383, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 433, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 384, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 433, + "placetype_component": 2, + "percent": 0.1914707 + } + }, + { + "pk": 385, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 433, + "placetype_component": 1, + "percent": 0.1000000 + } + }, + { + "pk": 386, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 342, + "percent": 0.0112353 + } + }, + { + "pk": 387, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 371, + "percent": 0.0112353 + } + }, + { + "pk": 388, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 385, + "percent": 0.0112353 + } + }, + { + "pk": 389, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 398, + "percent": 0.0112353 + } + }, + { + "pk": 390, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 389, + "percent": 0.0337058 + } + }, + { + "pk": 391, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 347, + "percent": 0.0112353 + } + }, + { + "pk": 392, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 400, + "percent": 0.4000000 + } + }, + { + "pk": 393, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 370, + "percent": 0.0224705 + } + }, + { + "pk": 394, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 361, + "percent": 0.0674115 + } + }, + { + "pk": 395, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 354, + "percent": 0.0224705 + } + }, + { + "pk": 396, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 364, + "percent": 0.0112353 + } + }, + { + "pk": 397, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 365, + "percent": 0.0112353 + } + }, + { + "pk": 398, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 399, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 2, + "percent": 0.2552949 + } + }, + { + "pk": 400, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 434, + "placetype_component": 1, + "percent": 0.1000000 + } + }, + { + "pk": 401, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 435, + "placetype_component": 389, + "percent": 0.0915250 + } + }, + { + "pk": 402, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 435, + "placetype_component": 3, + "percent": 0.0200000 + } + }, + { + "pk": 403, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 435, + "placetype_component": 2, + "percent": 0.1084750 + } + }, + { + "pk": 404, + "model": "main.placetypecomponentpercent", + "fields": { + "placetype": 435, + "placetype_component": 1, + "percent": 0.7800000 + } + } +] \ No newline at end of file diff --git a/footprint/client/configuration/default/built_form/json_fixtures/sacog_placetype.json b/footprint/client/configuration/default/built_form/json_fixtures/sacog_placetype.json new file mode 100644 index 000000000..91b2d5399 --- /dev/null +++ b/footprint/client/configuration/default/built_form/json_fixtures/sacog_placetype.json @@ -0,0 +1,3067 @@ +[ + { + "pk": 8, + "model": "main.tag", + "fields": { + "tag": "Unsorted" + } + }, + { + "pk": 1, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": true, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": true, + "land_use": "Rural Residential", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 1.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 2, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": true, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": true, + "land_use": "Farm Home", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 1.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 3, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 1.10, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": true, + "land_use": "Very Low Density Detached Residential", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 4.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 4, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 4.10, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": true, + "land_use": "Low Density Detached Residential", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 8.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 5, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 8.10, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": true, + "land_use": "Medium Density Detached Residential", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 12.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 6, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 12.10, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": true, + "land_use": "Medium-High Density Detached Residential", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 25.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 7, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 8.10, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Medium Density Attached Residential", + "attached_flag": true, + "pct_off_off": 0.00, + "max_du_ac": 12.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 8, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 12.10, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Medium-High Density Attached Residential", + "attached_flag": true, + "pct_off_off": 0.00, + "max_du_ac": 25.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 9, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": true, + "land_use": "Mobile Home Park", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 25.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 10, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 25.10, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "High Density Attached Residential", + "attached_flag": true, + "pct_off_off": 0.00, + "max_du_ac": 44.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 11, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 44.10, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Very High Density Attached Residential", + "attached_flag": true, + "pct_off_off": 0.00, + "max_du_ac": 82.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 12, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.33, + "pct_ret_rest": 0.33, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 6.00, + "rural_flag": false, + "min_du_ac": 82.10, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Urban Attached Residential", + "attached_flag": true, + "pct_off_off": 0.00, + "max_du_ac": 125.00, + "pct_ret_svc": 0.33 + } + }, + { + "pk": 13, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.16, + "pct_ret_rest": 0.16, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 75.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Residential/Retail Mixed Use Low", + "attached_flag": true, + "pct_off_off": 0.53, + "max_du_ac": 26.00, + "pct_ret_svc": 0.16 + } + }, + { + "pk": 14, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.21, + "pct_ret_rest": 0.21, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 47.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Residential/Retail Mixed Use High", + "attached_flag": true, + "pct_off_off": 0.38, + "max_du_ac": 55.00, + "pct_ret_svc": 0.21 + } + }, + { + "pk": 15, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 268.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "High-Rise Mixed Use", + "attached_flag": true, + "pct_off_off": 0.00, + "max_du_ac": 344.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 16, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 135.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Mid-Rise Mixed Use", + "attached_flag": true, + "pct_off_off": 0.00, + "max_du_ac": 198.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 17, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 37.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Urban Mid-Rise Residential", + "attached_flag": true, + "pct_off_off": 0.00, + "max_du_ac": 361.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 18, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.33, + "pct_ret_rest": 0.33, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 20.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Community/Neighborhood Retail", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.33 + } + }, + { + "pk": 19, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.44, + "pct_ret_ret": 0.50, + "pct_ret_rest": 0.06, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 28.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Community/Neighborhood Commercial", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 20, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.71, + "pct_ret_ret": 0.26, + "pct_ret_rest": 0.03, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 37.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Community/Neighborhood Commercial/Office", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 21, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.12, + "pct_ret_ret": 0.71, + "pct_ret_rest": 0.13, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 53.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Regional Retail", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.04 + } + }, + { + "pk": 22, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.40, + "pct_ret_ret": 0.54, + "pct_ret_rest": 0.06, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 50.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Regional Comercial/Office", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 23, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 150.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Hotel", + "attached_flag": false, + "pct_off_off": 0.14, + "max_du_ac": 0.00, + "pct_ret_svc": 0.86 + } + }, + { + "pk": 24, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.44, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.01, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 180.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.10, + "detached_flag": false, + "land_use": "Moderate-Intensity Office", + "attached_flag": false, + "pct_off_off": 0.44, + "max_du_ac": 0.00, + "pct_ret_svc": 0.01 + } + }, + { + "pk": 25, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.01, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 294.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.34, + "detached_flag": false, + "land_use": "High-Intensity Office", + "attached_flag": false, + "pct_off_off": 0.64, + "max_du_ac": 0.00, + "pct_ret_svc": 0.01 + } + }, + { + "pk": 26, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.01, + "pct_ret_rest": 0.01, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 711.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.15, + "detached_flag": false, + "land_use": "CBD Office", + "attached_flag": false, + "pct_off_off": 0.84, + "max_du_ac": 0.00, + "pct_ret_svc": 0.01 + } + }, + { + "pk": 27, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.01, + "pct_ret_rest": 0.01, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.10, + "max_emp_ac": 44.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Light Industrial-Office", + "attached_flag": false, + "pct_off_off": 0.85, + "max_du_ac": 0.00, + "pct_ret_svc": 0.01 + } + }, + { + "pk": 28, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.04, + "pct_ret_rest": 0.04, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.54, + "max_emp_ac": 27.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Light Industrial", + "attached_flag": false, + "pct_off_off": 0.34, + "max_du_ac": 0.00, + "pct_ret_svc": 0.04 + } + }, + { + "pk": 29, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 1.00, + "max_emp_ac": 11.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Heavy Industrial", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 30, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.91, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.09, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 176.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Medical Facility", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 31, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 1.00, + "pct_ind": 0.00, + "max_emp_ac": 20.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "K-12 School", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 32, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 1.00, + "pct_ind": 0.00, + "max_emp_ac": 100.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Colleges and Universities", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 33, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.05, + "pct_off_med": 0.00, + "pct_off_svc": 0.95, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 20.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Civic/Institution", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 34, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 1.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 20.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Public/Quasi-Public", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 35, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Parking Lot", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 36, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.33, + "pct_ret_rest": 0.33, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 41.50, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Parking Structure w/Ground Floor Retail", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.33 + } + }, + { + "pk": 37, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.33, + "pct_ret_rest": 0.33, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 2.40, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Parking Structure", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.33 + } + }, + { + "pk": 38, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.27, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.64, + "max_emp_ac": 7.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Agricultural Processing/Retail Employment", + "attached_flag": false, + "pct_off_off": 0.10, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 39, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.05, + "pct_ret_rest": 0.08, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.39, + "max_emp_ac": 2.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.10, + "detached_flag": false, + "land_use": "Airport", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.39 + } + }, + { + "pk": 40, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.30, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 1.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Military", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 41, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Park and/or Open Space", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 42, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Urban Reserve", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 43, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Road", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 44, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Water", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 45, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Blank Place Type", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 46, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Forest", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 47, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "Agriculture", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 0.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 48, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 0.00, + "pct_ind": 0.00, + "max_emp_ac": 0.00, + "rural_flag": true, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": true, + "land_use": "LARGE LOT NOT FARM HOME", + "attached_flag": false, + "pct_off_off": 0.00, + "max_du_ac": 1.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 49, + "model": "main.placetypeattributeset", + "fields": { + "pct_pub_gov": 0.00, + "pct_off_med": 0.00, + "pct_off_svc": 0.00, + "pct_ret_ret": 0.00, + "pct_ret_rest": 0.00, + "pct_pub_med": 0.00, + "pct_pub_edu": 1.00, + "pct_ind": 0.00, + "max_emp_ac": 64.00, + "rural_flag": false, + "min_du_ac": 0.00, + "pct_other": 0.00, + "pct_off_gov": 0.00, + "detached_flag": false, + "land_use": "UC DAVIS", + "attached_flag": true, + "pct_off_off": 0.00, + "max_du_ac": 3.00, + "pct_ret_svc": 0.00 + } + }, + { + "pk": 9, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Retail Services", + "description": null + } + }, + { + "pk": 5, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Office", + "description": null + } + }, + { + "pk": 3, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Retail", + "description": null + } + }, + { + "pk": 8, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Education Services", + "description": null + } + }, + { + "pk": 7, + "model": "main.buildingusedefinition", + "fields": { + "category": null, + "name": "Restaurant", + "description": null + } + }, + { + "pk": 405, + "model": "main.medium", + "fields": { + "key": "built_form_436", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 406, + "model": "main.medium", + "fields": { + "key": "built_form_437", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk5RkY5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 407, + "model": "main.medium", + "fields": { + "key": "built_form_438", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 408, + "model": "main.medium", + "fields": { + "key": "built_form_439", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkYwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 409, + "model": "main.medium", + "fields": { + "key": "built_form_440", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZBQkY4RnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 410, + "model": "main.medium", + "fields": { + "key": "built_form_441", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGQ0M2NnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 411, + "model": "main.medium", + "fields": { + "key": "built_form_443", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0YyNzkwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 412, + "model": "main.medium", + "fields": { + "key": "built_form_444", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGNjYwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 413, + "model": "main.medium", + "fields": { + "key": "built_form_445", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NDNjYwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 414, + "model": "main.medium", + "fields": { + "key": "built_form_446", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk5MzMwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 415, + "model": "main.medium", + "fields": { + "key": "built_form_447", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzdBMjkwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 416, + "model": "main.medium", + "fields": { + "key": "built_form_448", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzY2Q0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 417, + "model": "main.medium", + "fields": { + "key": "built_form_449", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzMzOTlGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 418, + "model": "main.medium", + "fields": { + "key": "built_form_450", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzY2RkZGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 419, + "model": "main.medium", + "fields": { + "key": "built_form_451", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzAwRkZGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 420, + "model": "main.medium", + "fields": { + "key": "built_form_452", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzAwOTk5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 421, + "model": "main.medium", + "fields": { + "key": "built_form_453", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZCMzMwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 458, + "model": "main.medium", + "fields": { + "key": "built_form_442", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOTkwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 459, + "model": "main.medium", + "fields": { + "key": "built_form_454", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGMDAwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 460, + "model": "main.medium", + "fields": { + "key": "built_form_455", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGMDAwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 461, + "model": "main.medium", + "fields": { + "key": "built_form_456", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NDMDAwMHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 462, + "model": "main.medium", + "fields": { + "key": "built_form_457", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 463, + "model": "main.medium", + "fields": { + "key": "built_form_458", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk2MzYzNHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 464, + "model": "main.medium", + "fields": { + "key": "built_form_459", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGOTlDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 465, + "model": "main.medium", + "fields": { + "key": "built_form_460", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGNjZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 466, + "model": "main.medium", + "fields": { + "key": "built_form_461", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGMzM5OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 467, + "model": "main.medium", + "fields": { + "key": "built_form_462", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NDOTlGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 468, + "model": "main.medium", + "fields": { + "key": "built_form_463", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk5MzNGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 469, + "model": "main.medium", + "fields": { + "key": "built_form_464", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk5MDBDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 470, + "model": "main.medium", + "fields": { + "key": "built_form_465", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzQwNDA0MHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 471, + "model": "main.medium", + "fields": { + "key": "built_form_466", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzgwODA4MHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 472, + "model": "main.medium", + "fields": { + "key": "built_form_467", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzU5NTk1OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 473, + "model": "main.medium", + "fields": { + "key": "built_form_468", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0JGQkZCRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 474, + "model": "main.medium", + "fields": { + "key": "built_form_469", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0Q5RDlEOXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 475, + "model": "main.medium", + "fields": { + "key": "built_form_470", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0M0QkQ5N3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 476, + "model": "main.medium", + "fields": { + "key": "built_form_471", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzQ5NDUyOXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 477, + "model": "main.medium", + "fields": { + "key": "built_form_472", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk0OEE1NHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 478, + "model": "main.medium", + "fields": { + "key": "built_form_473", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk0OEE1NHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 479, + "model": "main.medium", + "fields": { + "key": "built_form_474", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzI2MjYyNnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 480, + "model": "main.medium", + "fields": { + "key": "built_form_475", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0NDQ0NGRnEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 481, + "model": "main.medium", + "fields": { + "key": "built_form_476", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzkyRDA1MHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 482, + "model": "main.medium", + "fields": { + "key": "built_form_477", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 483, + "model": "main.medium", + "fields": { + "key": "built_form_478", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 484, + "model": "main.medium", + "fields": { + "key": "built_form_479", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 485, + "model": "main.medium", + "fields": { + "key": "built_form_480", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 486, + "model": "main.medium", + "fields": { + "key": "built_form_481", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBE5zcy4=", + "content_type": "color", + "description": null + } + }, + { + "pk": 487, + "model": "main.medium", + "fields": { + "key": "built_form_482", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzk0OEE1NHEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 488, + "model": "main.medium", + "fields": { + "key": "built_form_483", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHI0ZGRkZDQ3EFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 489, + "model": "main.medium", + "fields": { + "key": "built_form_484", + "name": "", + "url": null, + "content": "gAJ9cQFVBGZpbGxxAn1xA1UFY29sb3JxBFUHIzU5NTk1OXEFc3Mu", + "content_type": "color", + "description": null + } + }, + { + "pk": 436, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 405, + "name": "Rural Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 437, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 406, + "name": "Farm Home", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 438, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 407, + "name": "Very Low Density Detached Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 439, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 408, + "name": "Low Density Detached Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 440, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 409, + "name": "Medium Density Detached Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 443, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 411, + "name": "Medium-High Density Attached Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 444, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 412, + "name": "Mobile Home Park", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 445, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 413, + "name": "High Density Attached Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 446, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 414, + "name": "Very High Density Attached Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 447, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 415, + "name": "Urban Attached Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 448, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 416, + "name": "Residential/Retail Mixed Use Low", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 449, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 417, + "name": "Residential/Retail Mixed Use High", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 450, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 418, + "name": "High-Rise Mixed Use", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 451, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 419, + "name": "Mid-Rise Mixed Use", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 452, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 420, + "name": "Urban Mid-Rise Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 453, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 421, + "name": "Community/Neighborhood Retail", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 442, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 458, + "name": "Medium Density Attached Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 454, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 459, + "name": "Community/Neighborhood Commercial", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 456, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 461, + "name": "Regional Retail", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 457, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 462, + "name": "Regional Comercial/Office", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 458, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 463, + "name": "Hotel", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 459, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 464, + "name": "Moderate-Intensity Office", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 460, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 465, + "name": "High-Intensity Office", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 461, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 466, + "name": "CBD Office", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 462, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 467, + "name": "Light Industrial-Office", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 463, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 468, + "name": "Light Industrial", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 464, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 469, + "name": "Heavy Industrial", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 465, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 470, + "name": "Medical Facility", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 466, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 471, + "name": "K-12 School", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 467, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 472, + "name": "Colleges and Universities", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 468, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 473, + "name": "Civic/Institution", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 469, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 474, + "name": "Public/Quasi-Public", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 470, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 475, + "name": "Parking Lot", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 472, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 477, + "name": "Parking Structure", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 473, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 478, + "name": "Agricultural Processing/Retail Employment", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 474, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 479, + "name": "Airport", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 475, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 480, + "name": "Military", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 476, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 481, + "name": "Park and/or Open Space", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 477, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 482, + "name": "Urban Reserve", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 478, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 483, + "name": "Road", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 479, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 484, + "name": "Water", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 480, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 485, + "name": "Blank Place Type", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 481, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 486, + "name": "Forest", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 482, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 487, + "name": "Agriculture", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 483, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 488, + "name": "LARGE LOT NOT FARM HOME", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 441, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 410, + "name": "Medium-High Density Detached Residential", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 455, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 460, + "name": "Community/Neighborhood Commercial/Office", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 471, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 476, + "name": "Parking Structure w/Ground Floor Retail", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 484, + "model": "main.builtform", + "fields": { + "origin_built_form": null, + "medium": 489, + "name": "UC DAVIS", + "tags": [ + 8 + ], + "description": null + } + }, + { + "pk": 2, + "model": "main.buildingattributeset", + "fields": { + "parking_spaces": 25.900, + "combined_pop_emp_density": 39.0717, + "residential_irrigated_square_feet": 0.00, + "pervious_hardscape_percent": 0.050, + "hardscape_percent": 0.850, + "impervious_roof_percent": 0.500, + "softscape_and_landscape_percent": 0.100, + "impervious_hardscape_percent": 0.350, + "parking_structure_square_feet": 0.00, + "gross_net_ratio": 1.0000000, + "irrigated_percent": 0.900, + "household_density": 0.000, + "total_far": 0.7000000, + "floors": 5.000, + "residential_average_lot_size": 0.00, + "commercial_irrigated_square_feet": 3920.40, + "intersection_density": 0.0000, + "gross_population_density": 0.000 + } + }, + { + "pk": 436, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 1, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 437, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 2, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 438, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 3, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 439, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 4, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 440, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 5, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 441, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 6, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 442, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 7, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 443, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 8, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 444, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 9, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 445, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 10, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 446, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 11, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 447, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 12, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 448, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 13, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 449, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 14, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 450, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 15, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 451, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 16, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 452, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 17, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 453, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 18, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 454, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 19, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 455, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 20, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 456, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 21, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 457, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 22, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 458, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 23, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 459, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 24, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 460, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 25, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 461, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 26, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 462, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 27, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 463, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 28, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 464, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 29, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 465, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 30, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 466, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 31, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 467, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 32, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 468, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 33, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 469, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 34, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 470, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 35, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 471, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 36, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 472, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 37, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 473, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 38, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 474, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 39, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 475, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 40, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 476, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 41, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 477, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 42, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 478, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 43, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 479, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 44, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 480, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 45, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 481, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 46, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 482, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 47, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 483, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 48, + "tags": [ + 8 + ], + "building_attributes": 2 + } + }, + { + "pk": 484, + "model": "main.sacogplacetype", + "fields": { + "placetype_attribute_set": 49, + "tags": [ + 8 + ], + "building_attributes": 2 + } + } +] \ No newline at end of file diff --git a/footprint/client/configuration/default/built_form/old_placetypes.py b/footprint/client/configuration/default/built_form/old_placetypes.py new file mode 100644 index 000000000..b3528defc --- /dev/null +++ b/footprint/client/configuration/default/built_form/old_placetypes.py @@ -0,0 +1,1065 @@ +__author__ = 'calthorpe' + + +sample_placetypes = [ + (1,"Mixed Use Centers & Corridors","Urban Mixed Use"), + (2,"Mixed Use Centers & Corridors","Urban Residential"), + (3,"Mixed Use Centers & Corridors","Urban Commercial"), + (4,"Mixed Use Centers & Corridors","City Mixed Use"), + (5,"Mixed Use Centers & Corridors","City Residential"), + (6,"Mixed Use Centers & Corridors","City Commercial"), + (7,"Mixed Use Centers & Corridors","Town Mixed Use"), + (8,"Mixed Use Centers & Corridors","Town Residential"), + (9,"Mixed Use Centers & Corridors","Town Commercial"), + (10,"Mixed Use Centers & Corridors","Village Mixed Use"), + (11,"Mixed Use Centers & Corridors","Village Residential"), + (12,"Mixed Use Centers & Corridors","Village Commercial"), + (13,"Mixed Use Centers & Corridors","Neighborhood Residential"), + (14,"Mixed Use Centers & Corridors","Neighborhood Low"), + (15,"Employment Areas","Office Focus"), + (16,"Employment Areas","Mixed Office and R&D"), + (17,"Employment Areas","Office/Industrial"), + (18,"Employment Areas","Industrial Focus"), + (19,"Employment Areas","Low-Density Employment Park"), + (20,"Suburban","High Intensity Activity Center"), + (21,"Suburban","Mid Intensity Activity Center"), + (22,"Suburban","Low Intensity Retail-Centered N'Hood"), + (23,"Suburban","Retail: Strip Mall/ Big Box"), + (24,"Suburban","Industrial/Office/Res Mixed High"), + (25,"Suburban","Industrial/Office/Res Mixed Low"), + (26,"Suburban Residential","Suburban Multifamily"), + (27,"Suburban Residential","Suburban Mixed Residential"), + (28,"Suburban Residential","Residential Subdivision"), + (29,"Suburban Residential","Large Lot Residential Area"), + (30,"Rural","Rural Residential"), + (31,"Rural","Rural Ranchettes"), + (32,"Rural","Rural Employment"), + (33,"Institutional","Campus/ University"), + (34,"Institutional","Institutional"), + (35,"Parks","Parks & Open Space") +] + +# Create a dict {buildingtype_name:percent, ...} for the given placetype index +def building_type_percent_mix(placetype_index): + return map_dict_to_dict(lambda key, percents: [key, percents[placetype_index]], + filter_dict(lambda key, percents: percents[placetype_index] != 0, sample_placetype_buildingtype_mix)) + +# Each key here is a buildingtype name. Each array contains a percent of each of the placetypes. Note that some super categories like "MIXED USE" won't be used, but were part of the import process +sample_placetype_buildingtype_mix = { + #"MIXED USE":[0.01*57.0,0.01*15.0,0.01*12.0,0.01*44.0,0.01*14.0,0.01*5.0,0.01*38.0,0.01*12.5,0.01*20.0,0.01*20.0,0.01*0.0,0.01*10.0,0.01*2.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*45.0,0.01*5.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0], + "Skyscraper Mixed Use":[0.01*7,0.01*0,0.01*1,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "High-Rise Mixed Use":[0.01*15,0.01*2,0.01*1,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Mid-Rise Mixed Use":[0.01*17,0.01*3,0.01*2,0.01*10,0.01*2,0.01*1,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Low-Rise Mixed Use":[0.01*12,0.01*3,0.01*3,0.01*9,0.01*4,0.01*1,0.01*10,0.01*0,0.01*2,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Parking Structure/Mixed Use":[0.01*0,0.01*2,0.01*0,0.01*5,0.01*1,0.01*1,0.01*5,0.01*0,0.01*3,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Main Street Commercial/MU High (3-5 Floors)":[0.01*3,0.01*5,0.01*5,0.01*15,0.01*5,0.01*1,0.01*13,0.01*0,0.01*5,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*40,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Main Street Commercial/MU Low (1-2 Floors)":[0.01*3,0.01*0,0.01*0,0.01*5,0.01*2,0.01*1,0.01*10,0.01*13,0.01*10,0.01*15,0.01*0,0.01*10,0.01*2,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + #"RESIDENTIAL":[0.01*23.0,0.01*80.0,0.01*0.0,0.01*35.0,0.01*81.0,0.01*0.0,0.01*35.0,0.01*87.5,0.01*0.0,0.01*60.0,0.01*100.0,0.01*0.0,0.01*98.0,0.01*100.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*15.0,0.01*25.0,0.01*58.0,0.01*0.0,0.01*62.0,0.01*45.0,0.01*100.0,0.01*95.0,0.01*95.0,0.01*97.0,0.01*99.0,0.01*99.0,0.01*5.0,0.01*95.0,0.01*15.0,0.01*0.0], + "Skyscraper Residential":[0.01*5,0.01*12,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "High-Rise Residential":[0.01*7,0.01*15,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Urban Mid-Rise Residential":[0.01*8,0.01*34,0.01*0,0.01*10,0.01*20,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*21,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*45,0.01*0,0.01*0], + "Urban Podium Multi-Family":[0.01*3,0.01*14,0.01*0,0.01*10,0.01*26,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*25,0.01*5,0.01*0], + "Standard Podium Multi-Family":[0.01*0,0.01*5,0.01*0,0.01*5,0.01*10,0.01*0,0.01*5,0.01*20,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*11,0.01*0,0.01*35,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*25,0.01*0,0.01*0], + "Suburban Multifamily Apt/Condo":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*5,0.01*2,0.01*0,0.01*9,0.01*10,0.01*40,0.01*20,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Urban Townhome/Live-Work":[0.01*0,0.01*0,0.01*0,0.01*10,0.01*20,0.01*0,0.01*20,0.01*45,0.01*0,0.01*30,0.01*55,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Standard Townhome":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*5,0.01*0,0.01*0,0.01*11,0.01*15,0.01*20,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Garden Apartment":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*13,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*15,0.01*3,0.01*0,0.01*0,0.01*11,0.01*5,0.01*15,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Very Small Lot 3000":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*35,0.01*0,0.01*68,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Small Lot 4000":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*20,0.01*10,0.01*0,0.01*25,0.01*30,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*15,0.01*0,0.01*0,0.01*4,0.01*0,0.01*18,0.01*35,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*0], + "Medium Lot 5500":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*20,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*28,0.01*0,0.01*0,0.01*0,0.01*0,0.01*15,0.01*50,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Large Lot 7500":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*50,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*12,0.01*5,0.01*40,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Estate Lot":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*30,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Rural Residential":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*27,0.01*45,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Rural Ranchette":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*49,0.01*99,0.01*5,0.01*0,0.01*0,0.01*0], + #"COMMERCIAL/INDUSTRIAL":[0.01*20.0,0.01*5.0,0.01*88.0,0.01*21.0,0.01*5.0,0.01*95.0,0.01*27.0,0.01*0.0,0.01*80.0,0.01*20.0,0.01*0.0,0.01*90.0,0.01*0.0,0.01*0.0,0.01*100.0,0.01*100.0,0.01*100.0,0.01*100.0,0.01*100.0,0.01*40.0,0.01*70.0,0.01*42.0,0.01*100.0,0.01*38.0,0.01*55.0,0.01*0.0,0.01*5.0,0.01*5.0,0.01*3.0,0.01*0.0,0.01*1.0,0.01*95.0,0.01*5.0,0.01*85.0,0.01*100.0], + "Skyscraper Office":[0.01*0,0.01*0,0.01*8,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "High-Rise Office":[0.01*5,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Mid-Rise Office":[0.01*0,0.01*0,0.01*13,0.01*2,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Low-Rise Office":[0.01*0,0.01*0,0.01*39,0.01*3,0.01*0,0.01*42,0.01*19,0.01*0,0.01*15,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*8,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*30,0.01*0], + "Main Street Commercial (Retail + Office/Medical)":[0.01*5,0.01*0,0.01*8,0.01*5,0.01*0,0.01*31,0.01*5,0.01*0,0.01*50,0.01*15,0.01*0,0.01*80,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*25,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Parking Structure+Ground-Floor Retail":[0.01*5,0.01*0,0.01*5,0.01*3,0.01*0,0.01*5,0.01*0,0.01*0,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Parking Structure":[0.01*0,0.01*0,0.01*0,0.01*3,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*10,0.01*0], + "Office Park High":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*50,0.01*31,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Office Park Low":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*33,0.01*20,0.01*0,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Industrial High":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*8,0.01*5,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0], + "Industrial Low":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*3,0.01*15,0.01*20,0.01*20,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0], + "Warehouse High":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*10,0.01*7,0.01*12,0.01*10,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*9,0.01*24,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0], + "Warehouse Low":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*3,0.01*33,0.01*45,0.01*70,0.01*0,0.01*0,0.01*0,0.01*0,0.01*1,0.01*1,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0], + "Hotel High":[0.01*5,0.01*5,0.01*5,0.01*3,0.01*3,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Hotel Low":[0.01*0,0.01*0,0.01*0,0.01*2,0.01*2,0.01*2,0.01*3,0.01*0,0.01*3,0.01*5,0.01*0,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*2,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*100], + "Regional Mall":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*15,0.01*0,0.01*15,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Medium Intensity Strip Commercial (weighted avg)":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*2,0.01*0,0.01*0,0.01*3,0.01*0,0.01*0,0.01*20,0.01*5,0.01*3,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*50,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0], + "Low Intensity Strip Commercial (weighted avg)":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*2,0.01*0,0.01*0,0.01*0,0.01*5,0.01*7,0.01*20,0.01*5,0.01*0,0.01*30,0.01*35,0.01*35,0.01*0,0.01*0,0.01*0,0.01*5,0.01*5,0.01*3,0.01*0,0.01*0,0.01*0,0.01*0,0.01*5,0.01*0], + "Rural Employment":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*1,0.01*95,0.01*0,0.01*15,0.01*0], + #"INSTITUTIONAL (Itemized Civic)":[0.01*5.0,0.01*5.0,0.01*1.0,0.01*5.0,0.01*5.0,0.01*1.0,0.01*8.0,0.01*8.0,0.01*1.0,0.01*8.0,0.01*8.0,0.01*2.0,0.01*6.0,0.01*6.0,0.01*5.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*12.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*4.0,0.01*9.0,0.01*12.0,0.01*8.0,0.01*0.1,0.01*0.0,0.01*0.0,0.01*42.0,0.01*40.0,0.01*0.0], + "Campus/College High":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*40,0.01*0,0.01*0], + "Campus/College Low":[0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0], + "Hospital/Civic/Other Institutional":[0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*2,0.01*1,0.01*1,0.01*5,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*0,0.01*2,0.01*40,0.01*0], + "Urban Elementary School":[0.01*2.0,0.01*2.0,0.01*0.0,0.01*2.0,0.01*2.0,0.01*0.0,0.01*4.0,0.01*4.0,0.01*0.0,0.01*4.0,0.01*4.0,0.01*0.0,0.01*2.0,0.01*2.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0], + "Non-Urban Elementary School":[0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*6.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*2.0,0.01*3.0,0.01*6.0,0.01*4.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0], + "Urban Middle School":[0.01*1.0,0.01*1.0,0.01*0.0,0.01*1.0,0.01*1.0,0.01*0.0,0.01*2.0,0.01*2.0,0.01*0.0,0.01*2.0,0.01*2.0,0.01*0.0,0.01*2.0,0.01*2.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0], + "Non-Urban Middle School":[0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*4.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*1.0,0.01*3.0,0.01*3.0,0.01*2.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0], + "Urban High School":[0.01*1.0,0.01*1.0,0.01*0.0,0.01*1.0,0.01*1.0,0.01*0.0,0.01*1.0,0.01*1.0,0.01*0.0,0.01*1.0,0.01*1.0,0.01*0.0,0.01*1.0,0.01*1.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0], + "Non-Urban High School":[0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*2.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*1.0,0.01*3.0,0.01*3.0,0.01*2.0,0.01*0.1,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0,0.01*0.0], + #"STREETS/PARKS/OTHER CIVIC":[0.01*44.6,0.01*44.6,0.01*44.6,0.01*44.0,0.01*44.0,0.01*44.0,0.01*44.0,0.01*35.6,0.01*44.0,0.01*43.0,0.01*37.8,0.01*52.0,0.01*36.2,0.01*36.2,0.01*30.1,0.01*30.1,0.01*24.4,0.01*25.5,0.01*44.0,0.01*30.7,0.01*30.7,0.01*25.8,0.01*23.6,0.01*22.6,0.01*23.6,0.01*29.1,0.01*29.1,0.01*29.1,0.01*22.4,0.01*14.9,0.01*7.4,0.01*6.3,0.01*31.1,0.01*37.5,0.01*90.8], + #"Streets":[0.01*37,0.01*37,0.01*37,0.01*36,0.01*36,0.01*36,0.01*36,0.01*28,0.01*36,0.01*32,0.01*27,0.01*32,0.01*25,0.01*25,0.01*21,0.01*21,0.01*17,0.01*16,0.01*35,0.01*25,0.01*25,0.01*20,0.01*18,0.01*18,0.01*18,0.01*23,0.01*23,0.01*23,0.01*16,0.01*10,0.01*4,0.01*3,0.01*19,0.01*26,0.01*11], + #"Park":[0.01*7.0,0.01*7.0,0.01*7.0,0.01*7.0,0.01*7.0,0.01*7.0,0.01*7.0,0.01*7.0,0.01*7.0,0.01*10.0,0.01*10.0,0.01*15.0,0.01*10.0,0.01*10.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*3.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*4.0,0.01*1.0,0.01*1.0,0.01*1.0,0.01*10.0,0.01*10.0,0.01*78.0], + #"Detention/Utilities":[0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*1,0.01*5,0.01*1,0.01*1,0.01*5,0.01*5,0.01*3,0.01*5,0.01*5,0.01*2,0.01*2,0.01*2,0.01*2,0.01*2,0.01*2,0.01*2,0.01*2,0.01*2,0.01*2,0.01*4,0.01*2,0.01*2,0.01*2,0.01*2,0.01*2], + #"Total Civic":[0.01*6,0.01*6,0.01*2,0.01*6,0.01*6,0.01*2,0.01*9,0.01*9,0.01*2,0.01*9,0.01*9,0.01*7,0.01*7,0.01*7,0.01*10,0.01*5,0.01*3,0.01*5,0.01*5,0.01*2,0.01*2,0.01*14,0.01*2,0.01*2,0.01*2,0.01*6,0.01*11,0.01*14,0.01*10,0.01*4,0.01*2,0.01*2,0.01*44,0.01*42,0.01*2] +} + +# Creates a stucture {buildingtype_id=1, name=Building Type Name, buildings={name=Building Name, percent=Precent of Buildingtype}, ...} +sample_buildingtype_buildings = map(lambda tup: +{'buildingtype_id':tup[0], + 'name':tup[2][0], + 'buildings':map(lambda building_tup: { + 'name':building_tup[0], + 'percent':building_tup[1]}, tup[1]) +}, [ + (1, ( + ("181 Fremont Street",0.01*30), + ("Encinal Tower (Oakland, CA)",0.01*30), + ("Skyscraper Mixed (John Hancock Center, Chicago)",0.01*40), + ), + ("Skyscraper Mixed Use",0.01*100), + ), + (2, ( + ("High-Rise Mixed (Langstaff W-01)",0.01*0), + ("High-Rise Mixed (SCAG DT Mixed Use)",0.01*0), + ("High-Rise Residential (Visionaire, New York)",0.01*5), + ("Skyscraper Mixed (Presidential Towers, Chicago)",0.01*5), + ("High-Rise Mixed (The Infinity, San Francisco)",0.01*5), + ("High-Rise Mixed (201 Folsom, San Francisco)",0.01*7), + ("High-Rise Mixed (Atwater Place, Portland",0.01*28), + ("High-Rise Mixed (601 4th Ave, Seattle)",0.01*50), + ), + ("High-Rise Mixed Use",0.01*100), + ), + (3, ( + ("Mid-Rise Mixed (SCAG City Center MU)",0.01*0), + ("Mid-Rise Mixed (937 Glisan, Portland)",0.01*20), + ("Mid-Rise Mixed (The Edge, Portland)",0.01*40), + ("Mid-Rise Mixed (The Gregory Lofts, Portland)",0.01*40), + ), + ("Mid-Rise Mixed Use",0.01*100), + ), + (4, ( + ("Low-Rise Mixed (SCAG Dist. Center MU)",0.01*0), + ("Mid-Rise Mixed (Museum Place, Portland OR)",0.01*36), + ("Mid-Rise Mixed (Gaia Bldg, Berkeley)",0.01*2), + ("Mid-Rise Mixed (Fine Arts, Berkeley)",0.01*2), + ("Mid-Rise Mixed (East End Gateway, Sacramento)",0.01*2), + ("Mid-Rise Mixed (Site 17, Seattle)",0.01*2), + ("Mid-Rise Mixed (Alcyone, Seattle)",0.01*2), + ("Mid-Rise Mixed (1885 University/New Californian, Berkeley)",0.01*2), + ("Mid-Rise Mixed (Touriel Bldg, Berkeley)",0.01*2), + ("Low-Rise Mixed (Cap Metro City Center MU)",0.01*0), + ("Low-Rise Mixed (Stone Way Apts, Seattle)",0.01*20), + ("Low-Rise Mixed (200 Second Street, Oakland)",0.01*5), + ("Low-Rise Mixed (Cabrini First Hill Apts, Seattle)",0.01*5), + ("Low-Rise Mixed (Kinsey Flats, Cincinnati, OH)",0.01*15), + ("Low-Rise Mixed (Shattuck Lofts, Berkeley)",0.01*5), + ), + ("Low-Rise Mixed Use",0.01*100), + ), + (5, ( + ("Parking Structure/Mixed Use (Fahrenheit Condos + Petco Parkade, San Diego CA)",0.01*40), + ("Parking Structure/Mixed Use (2)",0.01*35), + ("Parking Structure/Mixed Use (3)",0.01*25), + ), + ("Parking Structure/Mixed Use",0.01*100), + ), + (6, ( + ("Main Street Commercial/MU (SACOG 19. MU Res Focus)",0.01*0), + ("Main Street Commercial/MU (SACOG 18. MU Emp Focus)",0.01*0), + ("Main Street Commercial/MU (SACOG 43. Natomas MU)",0.01*0), + ("Main Street Commercial/MU (3400 Cesar Chavez St, SF, CA)",0.01*10), + ("Main Street Commercial/MU (Belmont Dairy, Portland OR)",0.01*20), + ("Main Street Commercial/MU (Venice Renaissance, Venice CA)",0.01*10), + ("Main Street Commercial/MU (International Place, Harrisburg, PN)",0.01*40), + ("Main Street Commercial/MU (Heilig-Levine, Raleigh NC)",0.01*20), + ("Main Street Commercial/MU (SCAG Lifestyle Main Street)",0.01*0), + ), + ("Main Street Commercial/MU High (3-5 Floors)",0.01*100), + ), + (7, ( + ("Main Street Commercial/MU Low (2100 Vine Street, Berkeley, CA)",0.01*0), + ("Main Street Commercial/MU Low (480 Castro Street, San Francisco) (MU Walgreens)",0.01*50), + ("Fill Me 2-story low-density mixed use ground floor retail",0.01*0), + ("Fill Me 2-story low-density mixed use ground floor retail",0.01*0), + ("Main Street Commercial/MU Low (5488 College Avenue, Oakland) (MU Retail)",0.01*50), + ("Main Street Commercial/MU Low (300 30th Street at Church, San Francisco) (MU Church Produce)",0.01*0), + ), + ("Main Street Commercial/MU Low (1-2 Floors)",0.01*100), + ), + (8, ( + ("Skyscraper Residential (AC Hi Rise Res, 50+5 floors)",0.01*0), + ("Eureka Tower (Melbourne, AU)",0.01*0), + ("Millenium Tower (San Francisco)",0.01*0), + ("Skyscraper Residential (Rincon One, San Francisco)",0.01*50), + ("Skyscraper Residential (Langstaff W-12)",0.01*50), + ), + ("Skyscraper Residential",0.01*100), + ), + (9, ( + ("High-Rise Residential (AC Canal Tower Res, 20+5 floors)",0.01*0), + ("High-Rise Residential (SCAG DT Res, 30-50 floors))",0.01*0), + ("High-Rise Residential (AC Mid-Rise Res, 27+5 floors)",0.01*20), + ("High-Rise Residential (Pacifica Honolulu, Oahu)",0.01*10), + ("High-Rise Residential (Viridian, Nashville TN)",0.01*10), + ("High-Rise Residential (199 New Montgomery, SF)",0.01*20), + ("High-Rise Residential (The Metropolitan, SF)",0.01*20), + ("High-Rise Residential (Pine & Franklin, SF)",0.01*20), + ), + ("High-Rise Residential",0.01*100), + ), + (10, ( + ("Mid-Rise Residential (Langstaff E-09)",0.01*30), + ("Mid-Rise Mixed (Eddy + Taylor Family Housing, SF)",0.01*3), + ("Mid-Rise Mixed (Cubix Yerba Buena, SF)",0.01*1), + ("Mid-Rise Residential (AC Res Type 2/MU)",0.01*1), + ("Mid-Rise Residential (CapMetro Apt/Condo Hi)",0.01*65), + ), + ("Urban Mid-Rise Residential",0.01*100), + ), + (11, ( + ("Low-Rise Residential (AC Low Rise Res/MU)",0.01*10), + ("Low-Rise Residential (Alameda MF with Ret)",0.01*5), + ("Low-Rise Residential (SACOG 45. Intense Urban Res)",0.01*5), + ("Low-Rise Residential (SCAG Apt/Condo Hi)",0.01*5), + ("Low-Rise Residential (Avalon Apts (Cahill Park), San Jose)",0.01*0), + ("Low-Rise Residential (25-35 Dolores, SF)",0.01*20), + ("Low-Rise Residential (Ironhorse Family Apartments, Oakland)",0.01*10), + ("Low-Rise Residential (MODA Lofts, Stapleton, Denver)",0.01*25), + ("Low-Rise Residential (Darling Florist Bldg, Berkeley)",0.01*20), + ), + ("Urban Podium Multi-Family",0.01*100), + ), + (12, ( + ("Low-Rise Residential (AC Low Rise Res/MU)",0.01*0), + ("Low-Rise Residential (Alameda MF with Ret)",0.01*0), + ("Low-Rise Residential (SACOG 45. Intense Urban Res)",0.01*0), + ("Low-Rise Residential (SCAG Apt/Condo Hi)",0.01*0), + ("Low-Rise Residential (Avalon Apts (Cahill Park), San Jose)",0.01*55), + ("Low-Rise Residential (25-35 Dolores, SF)",0.01*0), + ("Low-Rise Residential (Ironhorse Family Apartments, Oakland)",0.01*20), + ("Low-Rise Residential (MODA Lofts, Stapleton, Denver)",0.01*25), + ("Low-Rise Residential (Darling Florist Bldg, Berkeley)",0.01*0), + ), + ("Standard Podium Multi-Family",0.01*100), + ), + (13, ( + ("Multifamily Apt/Condo (SEGA Apt/Condo Low)",0.01*0), + ("Multifamily Apt/Condo (SACOG 5. Med Hi-Den Res)",0.01*0), + ("Multifamily Apt/Condo (Lenzen Square, San Jose)",0.01*20), + ("Multifamily Apt/Condo (Linden Court, Oakland)",0.01*20), + ("Multi-Family Apt/Condo (Sonoma Villero, Bothell, WA)",0.01*20), + ("Multi-Family Apt/Condo (Town Lofts, Stapleton, Denver)",0.01*20), + ("Multifamily Apt/Condo (Mabuhay Court, San Jose)",0.01*20), + ("Multifamily Apt/Condo (SCAG Apt/Condo Med)",0.01*0), + ), + ("Suburban Multifamily Apt/Condo",0.01*100), + ), + (14, ( + ("Townhome/Live-Work (Alameda Small Townhouse)",0.01*10), + ("Townhome/Live-Work (SEGA Townhouse)",0.01*0), + ("Townhome/Live-Work (Denver Brownstone, Stapleton, Denver)",0.01*10), + ("Townhome/Live-Work (Pearl Townhome, Portland)",0.01*30), + ("Townhome/Live-Work (Penthouse Row Homes, Stapleton, Denver)",0.01*25), + ("Townhome/Live-Work (Backyard Row Home, Stapleton, Denver)",0.01*20), + ("Townhome/Live-Work (Sky Terrace, Stapleton, Denver)",0.01*5), + ("Townhome/Live-Work (SACOG D. Attached Res, 2-4 floors)",0.01*0), + ), + ("Urban Townhome/Live-Work",0.01*100), + ), + (15, ( + ("Townhome/Live-Work (Alameda Small Townhouse)",0.01*10), + ("Townhome/Live-Work (SEGA Townhouse)",0.01*25), + ("Townhome/Live-Work (Denver Brownstone, Stapleton, Denver)",0.01*0), + ("Townhome/Live-Work (Pearl Townhome, Portland)",0.01*0), + ("Townhome/Live-Work (Penthouse Row Homes, Stapleton, Denver)",0.01*0), + ("Townhome/Live-Work (Backyard Row Home, Stapleton, Denver)",0.01*20), + ("Townhome/Live-Work (Sky Terrace, Stapleton, Denver)",0.01*45), + ("Townhome/Live-Work (SACOG D. Attached Res, 2-4 floors)",0.01*0), + ), + ("Standard Townhome",0.01*100), + ), + (16, ( + ("Garden Apartment (Corte Bella, Irvine CA)",0.01*35), + ("Garden Apartment (Victoria Townhomes, Seattle WA)",0.01*30), + ("Stapleton Garden Apts (F1 Affordable Townhomes)",0.01*35), + ), + ("Garden Apartment",0.01*100), + ), + (17, ( + ("Very Small Lot 2500 (SEGA Res 2500 sf, 1-2 floors)",0.01*0), + ("Very Small Lot 2500 (The Boulders, Seattle WA)",0.01*0), + ("Very Small Lot 2500 (Inverness Square, Murray UT)",0.01*0), + ("Very Small Lot 2500 (Wild Sage Cohousing, Boulder CO)",0.01*0), + ("Find new 3000-square-foot lot single family @ ~15 du/acre",0.01*0), + ("Find new 3000-square-foot lot single family @ ~15 du/acre",0.01*0), + ("Very Small Lot 2500 (Discovery Collection at Riverdale, Sacramento)",0.01*0), + ("Very Small Lot 2500 (Coach House, Stapleton, Denver)",0.01*0), + ("Very Small Lot 2500 (Garden Courts, Stapleton, Denver)",0.01*100), + ), + ("Very Small Lot 3000",0.01*100), + ), + (18, ( + ("Small Lot 4000 (SEGA Res 4000 sf, 1-2 floors)",0.01*0), + ("Small Lot 4000 (SACOG C. SF Small Lot, 1-2 floors)",0.01*0), + ("Small Lot 4000 (John Laing SF, Stapleton, Denver)",0.01*30), + ("Small Lot 4000 (Town Square, Sapleton, Denver)",0.01*30), + ("Small Lot 4000 (Average, Albany)",0.01*30), + ("Small Lot 4000 (Alameda SF Detached, 1-2 floors)",0.01*10), + ), + ("Small Lot 4000",0.01*100), + ), + (19, ( + ("Medium Lot 5500 (SEGA Res 5500 sf, 1-2 floors)",0.01*0), + ("Medium Lot 5500 (SACOG 3. Low Den Res, 1-2 floors)",0.01*0), + ("Daybreak 5500",0.01*20), + ("Medium Lot 5500 (Laguna West-Plan 3, Laguna West)",0.01*0), + ("Medium Lot (Average, St. Francis Wood, San Francisco)",0.01*20), + ("Medium Lot 5500 (Brentwood, Brentwood)",0.01*0), + ("Medium Lot 5500 (Kentlands, Stapleton, Denver)",0.01*60), + ), + ("Medium Lot 5500",0.01*100), + ), + (20, ( + ("Large Lot 7500 sf (SEGA Res 7500 sf, 1-2 floors)",0.01*0), + ("Large Lot 7500 sf (SACOG B. SF Large Lot, 1-2 floors)",0.01*20), + ("Large Lot 7500 (Average, View Park, Los Angeles)",0.01*20), + ("Large Lot (Average, Gold Coast, Alameda, CA",0.01*20), + ("Large Lot 7500 (Estate Home, Stapleton, Denver)",0.01*40), + ), + ("Large Lot 7500",0.01*100), + ), + (21, ( + ("Estate Lot (SACOG 2. Very Low Den Res, , 1-2 floors)",0.01*10), + ("Estate Lot (SCAG Large Lot, 1-2 floors)",0.01*10), + ("Estate Lot (Average, Beverly Hills)",0.01*20), + ("Estate Lot (Average, Old Palo Alto)",0.01*20), + ("Estate Lot (Daybreak Estate, South Jordan)",0.01*20), + ("Estate Lot (Windemere Estate, San Ramon)",0.01*20), + ), + ("Estate Lot",0.01*100), + ), + (22, ( + ("Rural Residential (SACOG 1. Rural Res, 1-2 floors)",0.01*10), + ("Rural Residential (SCAG Rural, 1-2 floors)",0.01*40), + ("Rural Residential (Prairie Crossing Rural SF, Grayslake)",0.01*10), + ("Rural Residential (SEGA Rural, 1-2 floors)",0.01*40), + ), + ("Rural Residential",0.01*100), + ), + (23, ( + ("Rural/Ranchette (AFT 1.5 acre lot)",0.01*5), + ("Ranchette 1 (near Fresno)",0.01*5), + ("Ranchette 2",0.01*5), + ("Rural/Ranchette (AFT 5 acre lot)",0.01*5), + ("Ranchette 6 (Near Fresno)",0.01*10), + ("Rural/Ranchette (AFT 10 acre lot)",0.01*10), + ("Ranchette 4 (near Chowchilla)",0.01*10), + ("Rural/Ranchette (AFT 20 acre lot)",0.01*10), + ("Ranchette 5 (near Fresno)",0.01*20), + ("Ranchette 3 (near Boonville)",0.01*20), + ), + ("Rural Ranchette",0.01*100), + ), + (24, ( + ("Transbay Tower",0.01*20), + ("Skyscraper Office (US Bank Tower, Los Angeles)",0.01*20), + ("Skyscraper Office (Washington Mutual Tower, Seattle)",0.01*20), + ("Aon Center (Chicago, IL)",0.01*0), + ("Aon Center (Los Angeles, CA)",0.01*20), + ("Bank of America Center (Los Angeles, CA)",0.01*20), + ("Bank of America Tower (New York, NY)",0.01*0), + ("120 Collins Street (Melbourne, AU)",0.01*0), + ), + ("Skyscraper Office",0.01*100), + ), + (25, ( + ("High-Rise Office (AC Hi Rise Comm/MU, 36+5 floors)",0.01*0), + ("High-Rise Office (AC Mid Rise Comm/MU, 22+5 floors)",0.01*0), + ("High-Rise Mixed (Tabor Center, Denver)",0.01*30), + ("High-Rise Office (560 Mission Street, San Francisco)",0.01*20), + ("High Rise Office (555 Mission Street, San Francisco)",0.01*20), + ("High-Rise Office (55 Second Street, San Francisco)",0.01*10), + ("High-Rise Office (SACOG 46. CBD Ofice)",0.01*20), + ), + ("High-Rise Office",0.01*100), + ), + (26, ( + ("Mid-Rise Office (Langstaff W-05)",0.01*20), + ("Mid-Rise Mixed (AC Midrise Comm/MU)",0.01*0), + ("Mid-Rise Office (Langstaff W-04)",0.01*20), + ("Mid-Rise Office (AC Midrise Comm/MU, 10+4 floors)",0.01*0), + ("Mid-Rise Office (SCAG City Center Office, 6-15 floors)",0.01*20), + ("Mid-Rise Office (EPA Headquarters (Region 8), Denver)",0.01*20), + ("Mid-Rise Office (SACOG 8. Hi Intensity Office)",0.01*20), + ), + ("Mid-Rise Office",0.01*100), + ), + (27, ( + ("Low-Rise Office (AC Low Rise Office)",0.01*5), + ("Low-Rise Office (CalPERS Headquarters, Sacramento)",0.01*5), + ("Low-Rise Office (The Terry Thomas, Seattle)",0.01*20), + ("Low-Rise Office (223 Yale @ Alley24, Seattle)",0.01*20), + ("Low-Rise Office (Symantec Headquarters, Culver City)",0.01*30), + ("Low-Rise Office (SACOG 98. Mod Inten. Office)",0.01*5), + ("Low-Rise Office (R.D. Merrill Building, Seattle)",0.01*5), + ("Low-Rise Office (SEGA Low Rise Office, 4-6 floors)",0.01*10), + ), + ("Low-Rise Office",0.01*100), + ), + (28, ( + ("Main Street Commercial/MU Low (4185 Piedmont Avenue, Oakland) (Dentist Office)",0.01*5), + ("Main Street Commercial/MU Low (1853 Solano Avenue, Berkeley) (Zachary's Pizza)",0.01*5), + ("Main Street Commercial/MU Low (3170 College Avenue, Berkeley) (MU Noah's Bagels)",0.01*5), + ("Main Street Commercial (Mechanics Bank, Kensington CA)",0.01*20), + ("Main Street Commercial/MU Low (960 Cole Street, San Francisco) (Alpha Market)",0.01*25), + ("Main Street Commercial/MU Low (1601 N Main Street, Walnut Creek) (MU Instrument Sales)",0.01*25), + ("Main Street Commercial/MU Low (1616 N Main Street, Walnut Creek) (MU Crepe Vine)",0.01*15), + ), + ("Main Street Commercial (Retail + Office/Medical)",0.01*100), + ), + (29, ( + ("Parking Structure + Ground-Floor Retail (15th and Pearl Structure, Boulder, CO))",0.01*30), + ("Parking Structure + Ground-Floor Retail (8th and Hope, Los Angeles CA)",0.01*30), + ("Parking Structure + Ground-Floor Retail (3)",0.01*40), + ), + ("Parking Structure+Ground-Floor Retail",0.01*100), + ), + (30, ( + ("Parking Structure (1)",0.01*20), + ("Parking Structure (Oak & Central, Alameda)",0.01*20), + ("Parking Structure (2)",0.01*20), + ("Parking Structure (Jack London Market, Oakland)",0.01*20), + ("Parking Structure (3)",0.01*20), + ), + ("Parking Structure",0.01*100), + ), + (31, ( + ("Office Park High (AC Low Rise Office)",0.01*0), + ("Office Park High (SCAG Office Park, 2-4 floors)",0.01*0), + ("Office Park High (SACOG Light Indus/Office, 2-4 floors)",0.01*0), + ("Office Park High (SEGA Office Park 0.35, 2-4 floors)",0.01*10), + ("Office Park High (SACOG 98. Mod Inten. Office)",0.01*30), + ("Office Park High (Bishop Ranch BR-3, San Ramon)",0.01*20), + ("Office Park High (Bishop Ranch BR-6, San Ramon",0.01*10), + ("Office Park High (SEGA Low Rise Office, 4-6 floors)",0.01*30), + ), + ("Office Park High",0.01*100), + ), + (32, ( + ("Office Park Low (Redwood Business Park, Petaluma)",0.01*25), + ("Office Park Low (Nanometrics Bldg, Milpitas)",0.01*10), + ("Office Park Low (Sonoma Technology Bldg, Petaluma)",0.01*50), + ("Office Park Low (Bestronics Bldg, San Jose)",0.01*15), + ), + ("Office Park Low",0.01*100), + ), + (33, ( + ("Industrial High (SEGA Flex R&D, 1-2 floors)",0.01*0), + ("Industrial High (SACOG 13. Light Indus, 1-2 floors)",0.01*0), + ("Harte-Hanks Building (Valencia Commerce Center)",0.01*15), + ("FedEx Building,Gateway Office Park (South SF)",0.01*10), + ("Industrial High (SF Produce Markets, San Francisco)",0.01*30), + ("Industrial High (Odwalla Distribution Center, Berkeley)",0.01*20), + ("Industrial High (Lyons Magnus Plant #1, Fresno)",0.01*25), + ("Industrial High (SCAG Light Indus, 1-2 floors)",0.01*0), + ), + ("Industrial High",0.01*100), + ), + (34, ( + ("Industrial Low (SEGA Heavy Ind, 1-2 floors)",0.01*20), + ("Industrial Low (SACOG 14. Heavy Indus, 1-2 floors)",0.01*5), + ("Industrial Low (Pacific Business Center, Fremont CA)",0.01*10), + ("Industrial Low (Tank Farm Light Industrial, San Luis Obispo)",0.01*35), + ("Industrial Low (SCAG Heavy Indus, 1-2 floors)",0.01*30), + ), + ("Industrial Low",0.01*100), + ), + (35, ( + ("120 11th Street, San Francisco, CA 94103",0.01*20), + ("1360 Egbert, San Francisco, CA 94124",0.01*20), + ("Dynagraphics - 300 NW 14th Avenue, Portland, OR 97209",0.01*5), + ("2181 NW Nicolai, Portland, OR 97210",0.01*5), + ("NW Trunk & Bag Building - 522 N Thompson, Portland, OR 97227",0.01*5), + ("McClaskey Building - 2755 NW 31st Avenue, Portland, OR 97210",0.01*20), + ("525 SE Pine St, Portland, OR 97214",0.01*5), + ("111 SE Madison Ave, Portland, OR 97214",0.01*5), + ("WorkSpace - 2150 Folsom, San Francisco, CA 94110",0.01*10), + ("1154-1158 Howard Street, San Francisco, CA 94103",0.01*5), + ), + ("Warehouse High",0.01*100), + ), + (36, ( + ("9040 Carroll Way, San Diego, CA 92121 (Propertyline.com)",0.01*50), + ("2003 West Avenue 140th, San Leandro, CA 94577 (Loopnet.com)",0.01*10), + ("2300 Cesar Chavez, San Francisco, CA 94124",0.01*10), + ("Warehouse 3 - Proposed Emeryville IKEA (in 1.2/1.6 FAR district)",0.01*30), + ), + ("Warehouse Low",0.01*100), + ), + (37, ( + ("Hotel High (Four Seasons, San Francisco)",0.01*40), + ("Hotel High (Walt Disney World Dolphin, Orlando)",0.01*20), + ("Hotel High (Sheraton Grand, Sacramento)",0.01*40), + ), + ("Hotel High",0.01*100), + ), + (38, ( + ("Hotel Low (Holiday Inn Express, Truckee)",0.01*30), + ("Hotel Low (La Quinta Inn, Redding)",0.01*30), + ("Hotel Low (Holiday Inn, Woodland Hills)",0.01*40), + ), + ("Hotel Low",0.01*100), + ), + (39, ( + ("Regional Mall (SEGA General Commerical, 1-2 floors)",0.01*10), + ("Regional Mall (SACOG 11. Regional Retail, 1-2 floors)",0.01*10), + ("Regional Mall (Montclair Plaza, San Bernardino)",0.01*20), + ("Regional Mall (Westfield Galleria, Roseville)",0.01*25), + ("Regional Mall (Westfield Mission Valley, San Diego)",0.01*25), + ("Regional Mall (SCAG Regional Mall, 1-2 floors)",0.01*10), + ), + ("Regional Mall",0.01*100), + ), + (40, ( + ("Strip Commercial (SACOG 10. Comm/Nhood Retail, 1-2 floors)",0.01*10), + ("Med-Intensity Strip Commercial (Plaza Cienega, Los Angeles)",0.01*20), + ("Med-Intensity Strip Commercial (Greenway Plaza, Yonkers NY)",0.01*20), + ("Med-Intensity Strip Commercial (Tanner Market, Pasadena)",0.01*20), + ("Strip Commercial (SCAG Strip Commerical, 1-2 floors)",0.01*10), + ("Strip Commercial (Cap Metro Strip Commerical, 1-2 floors)",0.01*20), + ), + ("Medium Intensity Strip Commercial (weighted avg)",0.01*100), + ), + (41, ( + ("Strip Commercial (Gilroy Crossing, Gilroy)",0.01*5), + ("Strip Commercial (Paso Robles Strip Retail, Paso Robles)",0.01*10), + ("Strip Commercial (Renaissance Center West, Las Vegas)",0.01*15), + ("Strip Commercial (Mission Viejo Commerce Center)",0.01*10), + ("Strip Commercial (Mechanics Bank, Kensington CA)",0.01*0), + ("Strip Commercial (Guernville Rd McDonald's, Santa Rosa CA)",0.01*40), + ("Strip Commercial (Stanford Ranch, Roseville)",0.01*20), + ), + ("Low Intensity Strip Commercial (weighted avg)",0.01*100), + ), + (42, ( + ("Oil Field",0.01*0), + ("Occidential Elk Hills Oil Field",0.01*5), + ("Farm",0.01*0), + ("Very Large Farm",0.01*0), + ("Large Farm (Near Watsonville)",0.01*5), + ("Mid-sized farm 1300x1300 (near Manteca)",0.01*3), + ("Small farm 1300x650 (near Modesto)",0.01*2), + ("Very Small Farm 650x650 (Near Modesto)",0.01*1), + ("Orchard",0.01*0), + ("Very Large Orchard (Near Tracy)",0.01*5), + ("Medium Orchard (Near Tracy)",0.01*3), + ("Small Orchard (Near Ojai)",0.01*2), + ("Organic Farm",0.01*0), + ("Large Organic Farm (Frog Hollow, Brentwood)",0.01*3), + ("Medium Organic Farm (Live Power Farm, Covelo)",0.01*2), + ("Small Organic Farm (Gospel Flat Farm, Bollinas)",0.01*1), + ("Livestock",0.01*0), + ("Livestock Farm: Grassfed beef (Chilleno Valley Ranch, Petaluma)",0.01*27), + ("Livestock Farm: Harris Ranch Feedlot (I-5 CA-145 Interchange)",0.01*20), + ("Vineyard",0.01*0), + ("Vineyard, Small (Martin Stelling Vineyard)",0.01*5), + ("Vineyward, Medium (Quintessa Vineyard)",0.01*5), + ("Vineyard, Large (Napa Valley Wine Company)",0.01*5), + ("Prison",0.01*0), + ("Recreation",0.01*0), + ("Resource Extraction",0.01*0), + ("Liberty Quarry Proposal (Temecula, CA)",0.01*5), + ("Wind Farms",0.01*0), + ("Castle & Cook Resorts Wind Farm Proposal (Lanai, HI)",0.01*1), + ), + ("Rural Employment",0.01*100), + ), + (43, ( + ("Campus/College High",0.01*0), + ("Campus/College High (LA City College, Los Angeles)",0.01*100), + ("",0.01*0), + ), + ("Campus/College High",0.01*100), + ), + (44, ( + ("Campus/College Low",0.01*100), + ("",0.01*0), + ("",0.01*0), + ), + ("Campus/College Low",0.01*100), + ), + (45, ( + ("Hospital/Civic/Other Institutional",0.01*0), + ("Hospital (Children's Hospital, Los Angeles)",0.01*100), + ("",0.01*0), + ), + ("Hospital/Civic/Other Institutional",0.01*100), + ), + (46, ( + ("Urban Elementary School",0.01*0), + ("Urban Elementary School (Horace Mann ES, San Jose)",0.01*100), + ("Urban Elementary School (Cragmont, Berkeley CA)",0.01*0), + ("Ibarra Elementary School (San Diego)",0.01*0), + ), + ("Urban Elementary School",0.01*100), + ), + (47, ( + ("Non-Urban Elementary School",0.01*100), + ("",0.01*0), + ("",0.01*0), + ), + ("Non-Urban Elementary School",0.01*100), + ), + (48, ( + ("Urban Middle School",0.01*0), + ("Urban Middle School (Willard, Berkeley CA)",0.01*0), + ("Central Los Angeles Middle School",0.01*100), + ), + ("Urban Middle School",0.01*100), + ), + (49, ( + ("Non-Urban Middle School",0.01*100), + ("",0.01*0), + ("",0.01*0), + ), + ("Non-Urban Middle School",0.01*100), + ), + (50, ( + ("Urban High School",0.01*0), + ("Urban High School (Berkeley High School, Berkeley CA)",0.01*0), + ("",0.01*100), + ), + ("Urban High School",0.01*100), + ), + (51, ( + ("Non-Urban High School",0.01*100), + ("",0.01*0), + ("",0.01*0), + ), + ("Non-Urban High School",0.01*100) + ) +]) + +sample_building_uses = OrderedDict([ + (1, (("181 Fremont Street",0.33,0,0.67,0), + ("Encinal Tower (Oakland, CA)",0.45,0.05,0.5,0), + ("Skyscraper Mixed (John Hancock Center, Chicago)",0.6,0.06,0.34,0) + )), + (2, (("High-Rise Mixed (Langstaff W-01)",0.64,0.04,0.32,0), + ("High-Rise Mixed (SCAG DT Mixed Use)",0.55,0.03,0.42,0), + ("High-Rise Residential (Visionaire, New York)",0.92,0.01,0.07,0), + ("Skyscraper Mixed (Presidential Towers, Chicago)",0.89,0.05,0.06,0), + ("High-Rise Mixed (The Infinity, San Francisco)",0.989,0.011,0,0), + ("High-Rise Mixed (201 Folsom, San Francisco)",0.972,0.028,0,0), + ("High-Rise Mixed (Atwater Place, Portland",0.971,0.029,0,0), + ("High-Rise Mixed (601 4th Ave, Seattle)",0.222,0.039,0.739,0), + )), + (3, (("Mid-Rise Mixed (SCAG City Center MU)",0.8,0.2,0,0), + ("Mid-Rise Mixed (937 Glisan, Portland)",0.938,0.062,0,0), + ("Mid-Rise Mixed (The Edge, Portland)",0.571,0.124,0.306,0), + ("Mid-Rise Mixed (The Gregory Lofts, Portland)",0.841,0.061,0.098,0), + )), + (4, (("Low-Rise Mixed (SCAG Dist. Center MU)",0.75,0.25,0,0), + ("Mid-Rise Mixed (Museum Place, Portland OR)",0.7,0.3,0,0), + ("Mid-Rise Mixed (Gaia Bldg, Berkeley)",0.87,0.13,0,0), + ("Mid-Rise Mixed (Fine Arts, Berkeley)",0.903,0.097,0,0), + ("Mid-Rise Mixed (East End Gateway, Sacramento)",0.932,0.068,0,0), + ("Mid-Rise Mixed (Site 17, Seattle)",0.976,0.024,0,0), + ("Mid-Rise Mixed (Alcyone, Seattle)",0.989,0.011,0,0), + ("Mid-Rise Mixed (1885 University/New Californian, Berkeley)",0.9,0.1,0,0), + ("Mid-Rise Mixed (Touriel Bldg, Berkeley)",0.938,0.062,0,0), + ("Low-Rise Mixed (Cap Metro City Center MU)",0.4,0.2,0.4,0), + ("Low-Rise Mixed (Stone Way Apts, Seattle)",0.903,0.097,0,0), + ("Low-Rise Mixed (200 Second Street, Oakland)",0.893,0.107,0,0), + ("Low-Rise Mixed (Cabrini First Hill Apts, Seattle)",0.854,0.146,0,0), + ("Low-Rise Mixed (Kinsey Flats, Cincinnati, OH)",0.761,0.238,0,0), + ("Low-Rise Mixed (Shattuck Lofts, Berkeley)",0.9,0.1,0,0), + )), + (5, (("Parking Structure/Mixed Use (Fahrenheit Condos + Petco Parkade, San Diego CA)",0.5,0.5,0,0), + ("Parking Structure/Mixed Use (2)",0.5,0.5,0,0), + ("Parking Structure/Mixed Use (3)",0.5,0.5,0,0), + )), + (6, (("Main Street Commercial/MU (SACOG 19. MU Res Focus)",0.7,0.25,0.05,0), + ("Main Street Commercial/MU (SACOG 18. MU Emp Focus)",0.45,0.4,0.15,0), + ("Main Street Commercial/MU (SACOG 43. Natomas MU)",0.7,0.25,0.05,0), + ("Main Street Commercial/MU (3400 Cesar Chavez St, SF, CA)",0.81,0.19,0,0), + ("Main Street Commercial/MU (Belmont Dairy, Portland OR)",0.78,0.22,0,0), + ("Main Street Commercial/MU (Venice Renaissance, Venice CA)",0.77,0.23,0,0), + ("Main Street Commercial/MU (International Place, Harrisburg, PN)",0.69,0.31,0,0), + ("Main Street Commercial/MU (Heilig-Levine, Raleigh NC)",0,0.48,0.52,0), + ("Main Street Commercial/MU (SCAG Lifestyle Main Street)",0.4,0.6,0,0), + )), + (7, (("Main Street Commercial/MU Low (2100 Vine Street, Berkeley, CA)",0.52,0.484139397,0,0), + ("Main Street Commercial/MU Low (480 Castro Street, San Francisco) (MU Walgreens)",0.52,0.48,0,0), + ("Fill Me 2-story low-density mixed use ground floor retail",0.52,0.48,0,0), + ("Fill Me 2-story low-density mixed use ground floor retail",0.52,0.48,0,0), + ("Main Street Commercial/MU Low (5488 College Avenue, Oakland) (MU Retail)",0.52,0.48,0,0), + ("Main Street Commercial/MU Low (300 30th Street at Church, San Francisco) (MU Church Produce)",0.6,0.4,0,0), + ("Main Street Commercial/MU Low (1-2 Floors)",0.536,0.464827879,0,0), + )), + (8, (("Skyscraper Residential (AC Hi Rise Res, 50+5 floors)",0.94,0.06,0,0), + ("Eureka Tower (Melbourne, AU)",1), + ("Millenium Tower (San Francisco)",1), + ("Skyscraper Residential (Rincon One, San Francisco)",1,0,0,0), + ("Skyscraper Residential (Langstaff W-12)",0.98,0.02,0,0), + )), + (9, (("High-Rise Residential (AC Canal Tower Res, 20+5 floors)",0.99,0.01,0,0), + ("High-Rise Residential (SCAG DT Res, 30-50 floors))",0.97,0.03,0,0), + ("High-Rise Residential (AC Mid-Rise Res, 27+5 floors)",0.94,0.06,0,0), + ("High-Rise Residential (Pacifica Honolulu, Oahu)",0.9740319,0,0.01038724,0.01558086), + ("High-Rise Residential (Viridian, Nashville TN)",0.98,0.02,0,0), + ("High-Rise Residential (199 New Montgomery, SF)",0.974,0.026,0,0), + ("High-Rise Residential (The Metropolitan, SF)",0.998,0.002,0,0), + ("High-Rise Residential (Pine & Franklin, SF)",0.985,0.015,0,0), + )), + (10, (("Mid-Rise Residential (Langstaff E-09)",0.96,0.02,0.02,0), + ("Mid-Rise Mixed (Eddy + Taylor Family Housing, SF)",0.922,0.078,0,0), + ("Mid-Rise Mixed (Cubix Yerba Buena, SF)",0.91,0.09,0,0), + ("Mid-Rise Residential (AC Res Type 2/MU)",0.94,0.06,0,0), + ("Mid-Rise Residential (CapMetro Apt/Condo Hi)",0.9,0.1,0,0), + )), + (11, (("Low-Rise Residential (AC Low Rise Res/MU)",0.97,0.03,0,0), + ("Low-Rise Residential (Alameda MF with Ret)",0.9,0.1,0,0), + ("Low-Rise Residential (SACOG 45. Intense Urban Res)",0.95,0.05,0,0), + ("Low-Rise Residential (SCAG Apt/Condo Hi)",1,0,0,0), + ("Low-Rise Residential (Avalon Apts (Cahill Park), San Jose)",0.92,0.080320916,0,0), + ("Low-Rise Residential (25-35 Dolores, SF)",1,0,0,0), + ("Low-Rise Residential (Ironhorse Family Apartments, Oakland)",1,0,0,0), + ("Low-Rise Residential (MODA Lofts, Stapleton, Denver)",1,0,0,0), + ("Low-Rise Residential (Darling Florist Bldg, Berkeley)",0.9,0.1,0,0), + )), + (12, (("Low-Rise Residential (AC Low Rise Res/MU)",0.97,0.03,0,0), + ("Low-Rise Residential (Alameda MF with Ret)",0.9,0.1,0,0), + ("Low-Rise Residential (SACOG 45. Intense Urban Res)",0.95,0.05,0,0), + ("Low-Rise Residential (SCAG Apt/Condo Hi)",1,0,0,0), + ("Low-Rise Residential (Avalon Apts (Cahill Park), San Jose)",0.92,0.080320916,0,0), + ("Low-Rise Residential (25-35 Dolores, SF)",1,0,0,0), + ("Low-Rise Residential (Ironhorse Family Apartments, Oakland)",1,0,0,0), + ("Low-Rise Residential (MODA Lofts, Stapleton, Denver)",1,0,0,0), + ("Low-Rise Residential (Darling Florist Bldg, Berkeley)",0.9,0.1,0,0), + )), + (13, (("Multifamily Apt/Condo (SEGA Apt/Condo Low)",1,0,0,0), + ("Multifamily Apt/Condo (SACOG 5. Med Hi-Den Res)",1,0,0,0), + ("Multifamily Apt/Condo (Lenzen Square, San Jose)",1,0,0,0), + ("Multifamily Apt/Condo (Linden Court, Oakland)",1,0,0,0), + ("Multi-Family Apt/Condo (Sonoma Villero, Bothell, WA)",1,0,0,0), + ("Multi-Family Apt/Condo (Town Lofts, Stapleton, Denver)",1,0,0,0), + ("Multifamily Apt/Condo (Mabuhay Court, San Jose)",1,0,0,0), + ("Multifamily Apt/Condo (SCAG Apt/Condo Med)",1,0,0,0), + ("Suburban Multifamily Apt/Condo ",1,0,0,0), + )), + (14, (("Townhome/Live-Work (Alameda Small Townhouse)",1,0,0,0), + ("Townhome/Live-Work (SEGA Townhouse)",1,0,0,0), + ("Townhome/Live-Work (Denver Brownstone, Stapleton, Denver)",1,0,0,0), + ("Townhome/Live-Work (Pearl Townhome, Portland)",1,0,0,0), + ("Townhome/Live-Work (Penthouse Row Homes, Stapleton, Denver)",1,0,0,0), + ("Townhome/Live-Work (Backyard Row Home, Stapleton, Denver)",1,0,0,0), + ("Townhome/Live-Work (Sky Terrace, Stapleton, Denver)",1,0,0,0), + ("Townhome/Live-Work (SACOG D. Attached Res, 2-4 floors)",1,0,0,0), + )), + (15, (("Townhome/Live-Work (Alameda Small Townhouse)",1,0,0,0), + ("Townhome/Live-Work (SEGA Townhouse)",1,0,0,0), + ("Townhome/Live-Work (Denver Brownstone, Stapleton, Denver)",1,0,0,0), + ("Townhome/Live-Work (Pearl Townhome, Portland)",1,0,0,0), + ("Townhome/Live-Work (Penthouse Row Homes, Stapleton, Denver)",1,0,0,0), + ("Townhome/Live-Work (Backyard Row Home, Stapleton, Denver)",1,0,0,0), + ("Townhome/Live-Work (Sky Terrace, Stapleton, Denver)",1,0,0,0), + ("Townhome/Live-Work (SACOG D. Attached Res, 2-4 floors)",1,0,0,0), + )), + (16, (("Garden Apartment (Corte Bella, Irvine CA)",1,0,0,0), + ("Garden Apartment (Victoria Townhomes, Seattle WA)",1,0,0,0), + ("Stapleton Garden Apts (F1 Affordable Townhomes)",1,0,0,0), + )), + (17, (("Very Small Lot 2500 (SEGA Res 2500 sf, 1-2 floors)",1,0,0,0), + ("Very Small Lot 2500 (The Boulders, Seattle WA)",1,0,0,0), + ("Very Small Lot 2500 (Inverness Square, Murray UT)",1,0,0,0), + ("Very Small Lot 2500 (Wild Sage Cohousing, Boulder CO)",1,0,0,0), + ("Find new 3000-square-foot lot single family @ ~15 du/acre",1,0,0,0), + ("Find new 3000-square-foot lot single family @ ~15 du/acre",1,0,0,0), + ("Very Small Lot 2500 (Discovery Collection at Riverdale, Sacramento)",1,0,0,0), + ("Very Small Lot 2500 (Coach House, Stapleton, Denver)",1,0,0,0), + ("Very Small Lot 2500 (Garden Courts, Stapleton, Denver)",1,0,0,0), + )), + (18, (("Small Lot 4000 (SEGA Res 4000 sf, 1-2 floors)",1,0,0,0), + ("Small Lot 4000 (SACOG C. SF Small Lot, 1-2 floors)",1,0,0,0), + ("Small Lot 4000 (John Laing SF, Stapleton, Denver)",1,0,0,0), + ("Small Lot 4000 (Town Square, Sapleton, Denver)",1,0,0,0), + ("Small Lot 4000 (Average, Albany)",1,0,0,0), + ("Small Lot 4000 (Alameda SF Detached, 1-2 floors)",1,0,0,0), + )), + (19, (("Medium Lot 5500 (SEGA Res 5500 sf, 1-2 floors)",1,0,0,0), + ("Medium Lot 5500 (SACOG 3. Low Den Res, 1-2 floors)",1,0,0,0), + ("Daybreak 5500",1,0,0,0), + ("Medium Lot 5500 (Laguna West-Plan 3, Laguna West)",1,0,0,0), + ("Medium Lot (Average, St. Francis Wood, San Francisco)",1,0,0,0), + ("Medium Lot 5500 (Brentwood, Brentwood)",1,0,0,0), + ("Medium Lot 5500 (Kentlands, Stapleton, Denver)",1,0,0,0), + )), + (20, (("Large Lot 7500 sf (SEGA Res 7500 sf, 1-2 floors)",1,0,0,0), + ("Large Lot 7500 sf (SACOG B. SF Large Lot, 1-2 floors)",1,0,0,0), + ("Large Lot 7500 (Average, View Park, Los Angeles)",1,0,0,0), + ("Large Lot (Average, Gold Coast, Alameda, CA",1,0,0,0), + ("Large Lot 7500 (Estate Home, Stapleton, Denver)",1,0,0,0), + )), + (21, (("Estate Lot (SACOG 2. Very Low Den Res, , 1-2 floors)",1,0,0,0), + ("Estate Lot (SCAG Large Lot, 1-2 floors)",1,0,0,0), + ("Estate Lot (Average, Beverly Hills)",1,0,0,0), + ("Estate Lot (Average, Old Palo Alto)",1,0,0,0), + ("Estate Lot (Daybreak Estate, South Jordan)",1,0,0,0), + ("Estate Lot (Windemere Estate, San Ramon)",1,0,0,0), + )), + (22, (("Rural Residential (SACOG 1. Rural Res, 1-2 floors)",1,0,0,0), + ("Rural Residential (SCAG Rural, 1-2 floors)",1,0,0,0), + ("Rural Residential (Prairie Crossing Rural SF, Grayslake)",1,0,0,0), + ("Rural Residential (SEGA Rural, 1-2 floors)",1,0,0,0), + )), + (23, (("Rural/Ranchette (AFT 1.5 acre lot)",1,0,0,0), + ("Ranchette 1 (near Fresno)",1,0,0,0), + ("Ranchette 2 ",1,0,0,0), + ("Rural/Ranchette (AFT 5 acre lot)",1,0,0,0), + ("Ranchette 6 (Near Fresno)",1,0,0,0), + ("Rural/Ranchette (AFT 10 acre lot)",1,0,0,0), + ("Ranchette 4 (near Chowchilla)",1,0,0,0), + ("Rural/Ranchette (AFT 20 acre lot)",1,0,0,0), + ("Ranchette 5 (near Fresno)",1,0,0,0), + ("Ranchette 3 (near Boonville)",1,0,0,0), + )), + (24, (("Transbay Tower",0,0.1,0.9,0), + ("Skyscraper Office (US Bank Tower, Los Angeles)",0,0.03,0.97,0), + ("Skyscraper Office (Washington Mutual Tower, Seattle)",0,0.03,0.97,0), + ("Aon Center (Chicago, IL)",0,0.02,0.98,0), + ("Aon Center (Los Angeles, CA)",0,0.02,0.98,0), + ("Bank of America Center (Los Angeles, CA)",0,0.02,0.98,0), + ("Bank of America Tower (New York, NY)",0,0.02,0.98,0), + ("120 Collins Street (Melbourne, AU)",0,0,1,0), + )), + (25, (("High-Rise Office (AC Hi Rise Comm/MU, 36+5 floors)",0,0.06,0.94,0), + ("High-Rise Office (AC Mid Rise Comm/MU, 22+5 floors)",0,0.08,0.92,0), + ("High-Rise Mixed (Tabor Center, Denver)",0,0.1,0.9,0), + ("High-Rise Office (560 Mission Street, San Francisco)",0,0.008,0.992,0), + ("High Rise Office (555 Mission Street, San Francisco)",0,0.006,0.994,0), + ("High-Rise Office (55 Second Street, San Francisco)",0,0,1,0), + ("High-Rise Office (SACOG 46. CBD Ofice)",0,0.05,0.95,0), + )), + (26, (("Mid-Rise Office (Langstaff W-05)",0,0,1,0), + ("Mid-Rise Mixed (AC Midrise Comm/MU)",0,0.1,0.9,0), + ("Mid-Rise Office (Langstaff W-04)",0,0.1,0.9,0), + ("Mid-Rise Office (AC Midrise Comm/MU, 10+4 floors)",0,0.1,0.9,0), + ("Mid-Rise Office (SCAG City Center Office, 6-15 floors)",0,0.05,0.95,0), + ("Mid-Rise Office (EPA Headquarters (Region 8), Denver)",0,0,1,0), + ("Mid-Rise Office (SACOG 8. Hi Intensity Office)",0,0.05,0.95,0), + )), + (27, (("Low-Rise Office (AC Low Rise Office)",0,0,1,0), + ("Low-Rise Office (CalPERS Headquarters, Sacramento)",0,0,1,0), + ("Low-Rise Office (The Terry Thomas, Seattle)",0,0.075,0.925,0), + ("Low-Rise Office (223 Yale @ Alley24, Seattle)",0,0.111,0.889,0), + ("Low-Rise Office (Symantec Headquarters, Culver City)",0,0,1,0), + ("Low-Rise Office (SACOG 98. Mod Inten. Office)",0,0.05,0.95,0), + ("Low-Rise Office (R.D. Merrill Building, Seattle)",0,0,1,0), + ("Low-Rise Office (SEGA Low Rise Office, 4-6 floors)",0,0,1,0), + )), + (28, (("Main Street Commercial/MU Low (4185 Piedmont Avenue, Oakland) (Dentist Office)",0,0,1,0), + ("Main Street Commercial/MU Low (1853 Solano Avenue, Berkeley) (Zachary's Pizza)",0,1,0,0), + ("Main Street Commercial/MU Low (3170 College Avenue, Berkeley) (MU Noah's Bagels)",0,0.5,0.5,0), + ("Main Street Commercial (Mechanics Bank, Kensington CA)",0,0.2,0.8,0), + ("Main Street Commercial/MU Low (960 Cole Street, San Francisco) (Alpha Market)",0,1,0,0), + ("Main Street Commercial/MU Low (1601 N Main Street, Walnut Creek) (MU Instrument Sales)",0,0.75,0.25,0), + ("Main Street Commercial/MU Low (1616 N Main Street, Walnut Creek) (MU Crepe Vine)",0,0.6,0.4,0), + )), + (29, (("Parking Structure + Ground-Floor Retail (15th and Pearl Structure, Boulder, CO))",0,0.5,0.5,0), + ("Parking Structure + Ground-Floor Retail (8th and Hope, Los Angeles CA)",0,1,0,0), + ("Parking Structure + Ground-Floor Retail (3)",0,1,0,0), + ("Parking Structure+Ground-Floor Retail",0,0.85,0.15,0), + )), + (30, (("Parking Structure (1)",0,1,0,0), + ("Parking Structure (Oak & Central, Alameda)",0,1,0,0), + ("Parking Structure (2)",0,1,0,0), + ("Parking Structure (Jack London Market, Oakland)",0,1,0,0), + ("Parking Structure (3)",0,1,0,0), + )), + (31, (("Office Park High (AC Low Rise Office)",0,0,1,0), + ("Office Park High (SCAG Office Park, 2-4 floors)",0,0,1,0), + ("Office Park High (SACOG Light Indus/Office, 2-4 floors)",0,0,0.75,0.25), + ("Office Park High (SEGA Office Park 0.35, 2-4 floors)",0,0,1,0), + ("Office Park High (SACOG 98. Mod Inten. Office)",0,0.05,0.95,0), + ("Office Park High (Bishop Ranch BR-3, San Ramon)",0,0,1,0), + ("Office Park High (Bishop Ranch BR-6, San Ramon",0,0,1,0), + ("Office Park High (SEGA Low Rise Office, 4-6 floors)",0,0,1,0), + )), + (32, (("Office Park Low (Redwood Business Park, Petaluma)",0,0,0.8,0.2), + ("Office Park Low (Nanometrics Bldg, Milpitas)",0,0,1,0), + ("Office Park Low (Sonoma Technology Bldg, Petaluma)",0,0,1,0), + ("Office Park Low (Bestronics Bldg, San Jose)",0,0,1,0), + )), + (33, (("Industrial High (SEGA Flex R&D, 1-2 floors)",0,0.02,0,0.98), + ("Industrial High (SACOG 13. Light Indus, 1-2 floors)",0,0,0.25,0.75), + ("Harte-Hanks Building (Valencia Commerce Center)",0,0,0.15,0.85), + ("FedEx Building,Gateway Office Park (South SF)",0,0,0,1), + ("Industrial High (SF Produce Markets, San Francisco)",0,0,0,1), + ("Industrial High (Odwalla Distribution Center, Berkeley)",0,0,0,1), + ("Industrial High (Lyons Magnus Plant #1, Fresno)",0,0,0,1), + ("Industrial High (SCAG Light Indus, 1-2 floors)",0,0.03,0,0.97), + )), + (34, (("Industrial Low (SEGA Heavy Ind, 1-2 floors)",0,0,0,1), + ("Industrial Low (SACOG 14. Heavy Indus, 1-2 floors)",0,0,0,1), + ("Industrial Low (Pacific Business Center, Fremont CA)",0,0,0,1), + ("Industrial Low (Tank Farm Light Industrial, San Luis Obispo)",0,0,0,1), + ("Industrial Low (SCAG Heavy Indus, 1-2 floors)",0,0.02,0,0.98), + )), + (35, (("120 11th Street, San Francisco, CA 94103",0,0,0.1547,0.8453), + ("1360 Egbert, San Francisco, CA 94124",0,0,0,1), + ("Dynagraphics - 300 NW 14th Avenue, Portland, OR 97209",0,0,0.3,0.7), + ("2181 NW Nicolai, Portland, OR 97210",0,0,0.3,0.7), + ("NW Trunk & Bag Building - 522 N Thompson, Portland, OR 97227",0,0,0,1), + ("McClaskey Building - 2755 NW 31st Avenue, Portland, OR 97210",0,0,0,1), + ("525 SE Pine St, Portland, OR 97214",0,0,0.5,0.5), + ("111 SE Madison Ave, Portland, OR 97214",0,0,0.3,0.7), + ("WorkSpace - 2150 Folsom, San Francisco, CA 94110",0,0,0,1), + ("1154-1158 Howard Street, San Francisco, CA 94103",0,0,0,1), + )), + (36, (("9040 Carroll Way, San Diego, CA 92121 (Propertyline.com)",0,0,0.15,0.85), + ("2003 West Avenue 140th, San Leandro, CA 94577 (Loopnet.com)",0,0,0.1,0.9), + ("2300 Cesar Chavez, San Francisco, CA 94124",0,0,0.1,0.9), + ("Warehouse 3 - Proposed Emeryville IKEA (in 1.2/1.6 FAR district)",0,0,0,1), + )), + (37, (("Hotel High (Four Seasons, San Francisco)",0.645,0.355,0,0), + ("Hotel High (Walt Disney World Dolphin, Orlando)",0.9,0.01,0.09,0), + ("Hotel High (Sheraton Grand, Sacramento)",0.843,0.157,0,0), + )), + (38, (("Hotel Low (Holiday Inn Express, Truckee)",0.95,0.05,0,0), + ("Hotel Low (La Quinta Inn, Redding)",0.95,0.05,0,0), + ("Hotel Low (Holiday Inn, Woodland Hills)",0.95,0.05,0,0), + )), + (39, (("Regional Mall (SEGA General Commerical, 1-2 floors)",0,1,0,0), + ("Regional Mall (SACOG 11. Regional Retail, 1-2 floors)",0,0.95,0.05,0), + ("Regional Mall (Montclair Plaza, San Bernardino)",0,1,0,0), + ("Regional Mall (Westfield Galleria, Roseville)",0,1,0,0), + ("Regional Mall (Westfield Mission Valley, San Diego)",0,1,0,0), + ("Regional Mall (SCAG Regional Mall, 1-2 floors)",0,1,0,0), + )), + (40, (("Strip Commercial (SACOG 10. Comm/Nhood Retail, 1-2 floors)",0,1,0,0), + ("Med-Intensity Strip Commercial (Plaza Cienega, Los Angeles)",0,1,0,0), + ("Med-Intensity Strip Commercial (Greenway Plaza, Yonkers NY)",0,1,0,0), + ("Med-Intensity Strip Commercial (Tanner Market, Pasadena)",0,0.955,0.045,0), + ("Strip Commercial (SCAG Strip Commerical, 1-2 floors)",0,0.7,0.3,0), + ("Strip Commercial (Cap Metro Strip Commerical, 1-2 floors)",0,0.6,0.4,0), + )), + (41, (("Strip Commercial (Gilroy Crossing, Gilroy)",0,1,0,0), + ("Strip Commercial (Paso Robles Strip Retail, Paso Robles)",0,1,0,0), + ("Strip Commercial (Renaissance Center West, Las Vegas)",0,0.983,0.017,0), + ("Strip Commercial (Mission Viejo Commerce Center)",0,1,0,0), + ("Strip Commercial (Mechanics Bank, Kensington CA)",0,1,0,0), + ("Strip Commercial (Guernville Rd McDonald's, Santa Rosa CA)",0,1,0,0), + ("Strip Commercial (Stanford Ranch, Roseville)",0,1,0,0), + )), + (42, (("Oil Field",0,0,0.05,0.95), + ("Occidential Elk Hills Oil Field",0,0,0,1), + ("Farm",0,0.05,0.05,0.9), + ("Very Large Farm",0.01,0,0,0.99), + ("Large Farm (Near Watsonville)",0.01,0,0,0.99), + ("Mid-sized farm 1300x1300 (near Manteca)",0.01,0,0,0.99), + ("Small farm 1300x650 (near Modesto)",0.02,0,0,0.99), + ("Very Small Farm 650x650 (Near Modesto)",0.03,0,0,0.99), + ("Orchard",0,0,0,1), + ("Very Large Orchard (Near Tracy)",0,0,0,1), + ("Medium Orchard (Near Tracy)",0,0,0,1), + ("Small Orchard (Near Ojai)",0.01,0,0,0.99), + ("Organic Farm",0,0,0,1), + ("Large Organic Farm (Frog Hollow, Brentwood)",0.01,0,0,0.99), + ("Medium Organic Farm (Live Power Farm, Covelo)",0.02,0,0,0.98), + ("Small Organic Farm (Gospel Flat Farm, Bollinas)",0.03,0,0,0.97), + ("Livestock",0,0,0,1), + ("Livestock Farm: Grassfed beef (Chilleno Valley Ranch, Petaluma)",0,0,0,1), + ("Livestock Farm: Harris Ranch Feedlot (I-5 CA-145 Interchange)",0,0,0,1), + ("Vineyard",0,0,0,1), + ("Vineyard, Small (Martin Stelling Vineyard)",0.03,0,0,0.97), + ("Vineyward, Medium (Quintessa Vineyard)",0.02,0,0,0.98), + ("Vineyard, Large (Napa Valley Wine Company)",0.01,0,0,0.99), + ("Prison",0,0,0,1), + ("Recreation",0,0,0.05,0.95), + ("Resource Extraction",0,0.05,0.1,0.85), + ("Liberty Quarry Proposal (Temecula, CA)",0,0,0,1), + ("Wind Farms",0,0,0.05,0.95), + ("Castle & Cook Resorts Wind Farm Proposal (Lanai, HI)",0,0,0,1), + )), + (43, ( + ("Campus/College High (LA City College, Los Angeles)",0,0,1,0), + )), + + (45,(("Hospital (Children's Hospital, Los Angeles)",0,0,1,0), + )), + (46, (("Urban Elementary School (Horace Mann ES, San Jose)",0,0,1), + ("Urban Elementary School (Cragmont, Berkeley CA)",0,0,1,0), + )), + + (48,(("Urban Middle School (Willard, Berkeley CA)",0,0,1,0), + ("Central Los Angeles Middle School",0,0,1), + )), + + (50, (("Urban High School (Berkeley High School, Berkeley CA)",0,0,1,0), + )), + (51, ( + ("Non-Urban High School ",0,0,1,0), + )), + (52, (("Urban City Hall (Oakland City Hall, Oakland, CA)",0,0,1,0), + ("Urban City Hall (Long Beach City Hall and Civic Center, Long Beach, CA)",0,0,1,0), + )), + (53, (("Urban Public Library - Main Branch (Oakland Public Library, Oakland, CA)",0,0,0,1), + )), + (54, (("Urban Courthouse (Rene C. Davidson Courthouse, Oakland, CA)",0,0,0,1), + ("Urban Courthouse (Long Beach Superior Court, Long Beach, CA)",0,0,0,1), + )), + (55, (("Urban Convention Center (Oakland Convention Center, Oakland, CA),",0.1,0.9), + ("Urban Convention Center (Long Beach Convention and Entertainment Center, Long Beach, CA),",0.2,0.8), + ("Urban Convention Center (San Diego Convention Center),",0.2,0.8), + )), + (56, (("Suburban Civic Complex (City Hall, Library, Rec Center, Menlo Park, CA)",0,0,0,1), + ("Suburban Civic Complex (City Hall, Library, Gym and Teen Center, Walnut, CA)",0,0,0,1), + ("Suburban Civic Buildings (Police Station, Community Services, Walnut, CA)",0,0,0,1), + )), + (57, (("Town Civic Complex (City Hall, Police Dept., St. Helena, CA)",0,0,0,1,0), + ("Town Civic Complex (City Hall, Police and Fire Dept., Bishop, CA)",0,0,0,1,0), + )), + (58, (("Town Library (St Helena Public Library, St. Helena, CA)",0,0,0,1,0), + ("Town Library (Bishop Branch Library, Bishop, CA)",0,0,0,1,0), + )), + (59, (("Church 1,",0.05,0.95,0), + ("Church 2,",0.05,0.95,0), + ("Church 3,",0.05,0.95,),) + )]) + +def construct_buildingtypes_old(): + buildings = [] + buildingtypes = [] + building_percents = [] + building_use_definitions = BuildingUseDefinition.objects.filter(name__in=[Keys.BUILDING_USE_INDUSTRIAL, + Keys.BUILDING_USE_OFFICE, + Keys.BUILDING_USE_RESIDENTIAL, + Keys.BUILDING_USE_RETAIL]) + building_use_percents = [] + + for buildingtype_dict in sample_buildingtype_buildings: + buildingtype = Buildingtype(name=buildingtype_dict['name']) + buildingtypes.append(buildingtype) + building_uses = sample_building_uses.get(buildingtype_dict['buildingtype_id'], []) + name_to_percent = map_to_dict(lambda building_use :(building_use[0], building_use[1:]), building_uses) + + for building_dict in buildingtype_dict['buildings']: + # Match the building name to our Building instances that were created from csv + import_building = imported_building_lookup.get(building_dict['name'], Building(name=building_dict['name'])) + buildings.append(import_building) + building_percent = BuildingPercent(buildingtype=buildingtype, building=import_building, percent=building_dict['percent']) + building_percents.append(building_percent) + percents = name_to_percent.get(import_building.name, None) + if percents: + building_use_percents.extend(dual_map(lambda building_use_percent, building_use_definition: BuildingUsePercent( + building=import_building, building_use_definition=building_use_definition, percent=building_use_percent), + percents, building_use_definitions)) + + return {'buildingtypes':buildingtypes, 'buildings':buildings, 'building_percents':building_percents, 'building_use_percents':building_use_percents} diff --git a/footprint/client/configuration/default/built_form/placetype_examples.py b/footprint/client/configuration/default/built_form/placetype_examples.py new file mode 100644 index 000000000..c54fdf5e6 --- /dev/null +++ b/footprint/client/configuration/default/built_form/placetype_examples.py @@ -0,0 +1,42 @@ +import json + +__author__ = 'calthorpe' +placetype_example_json = """{ + + "pt__town_commercial" : [ + { + "example_name" : "Downtown Ashland", + "example_url" : "http://www.ashland.or.us/" + }, + { + "example_name" : "Downtown Redwood City", + "example_url" : "http://www.redwoodcity.org/" + } + + ], + "pt__campus_university" : [ + { + "example_name" : "University of California, Berkeley", + "example_url" : "http://www.berkeley.edu/index.html" + }, + { + "example_name" : "University of Oregon", + "example_url" : "http://uoregon.edu/" + } + + ], + "default" : [ + { + "example_name" : "Placetype example region 1", + "example_url" : "http://www.berkeley.edu/index.html" + }, + { + "example_name" : "Placetype example region 2", + "example_url" : "http://uoregon.edu/" + } + + ] + }""" +PLACETYPE_EXAMPLES = json.loads(placetype_example_json) + + diff --git a/footprint/client/configuration/default/config_entity/__init__.py b/footprint/client/configuration/default/config_entity/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/default/config_entity/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/default/config_entity/default_config_entities.py b/footprint/client/configuration/default/config_entity/default_config_entities.py new file mode 100644 index 000000000..a84f602f1 --- /dev/null +++ b/footprint/client/configuration/default/config_entity/default_config_entities.py @@ -0,0 +1,68 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.client.configuration.fixture import ConfigEntitiesFixture +from footprint.client.configuration.default.default_mixin import DefaultMixin +from footprint.main.models.config.scenario import FutureScenario, BaseScenario +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +class ConfigEntityMediumKey(Keys): + class Fab(Keys.Fab): + @classmethod + def prefix(cls): + return 'config_entity_medium' + + +class DefaultConfigEntitiesFixture(DefaultMixin, ConfigEntitiesFixture): + def regions(self): + return [ + ] + + def projects(self, region=None): + return [ + ] + + def scenarios(self, project=None, class_scope=None): + return self.matching_keys([], project_key=project.key if project else None, class_scope=class_scope) + + # Fixtures for testing import/cloning + def import_scenarios(self, origin_config_entity): + return self.matching_scope([ + dict( + class_scope=FutureScenario, + key=origin_config_entity.key[0:10:]+'_future_clone', + name=origin_config_entity.name[0:10]+' Future Clone', + parent_config_entity=origin_config_entity.parent_config_entity, + origin_config_entity=origin_config_entity, + bounds=origin_config_entity.bounds, + description=origin_config_entity.description+' Future Clone', + year=origin_config_entity.year + ), + dict( + class_scope=BaseScenario, + key=origin_config_entity.key[0:10]+'_base_clone', + name=origin_config_entity.name[0:10]+' Base Clone', + parent_config_entity=origin_config_entity.parent_config_entity, + origin_config_entity=origin_config_entity, + bounds=origin_config_entity.bounds, + description=origin_config_entity.description+' Base Clone', + year=origin_config_entity.year + ) + ], class_scope=origin_config_entity.__class__) diff --git a/footprint/client/configuration/default/config_entity/default_global_config.py b/footprint/client/configuration/default/config_entity/default_global_config.py new file mode 100644 index 000000000..df65f7bdb --- /dev/null +++ b/footprint/client/configuration/default/config_entity/default_global_config.py @@ -0,0 +1,44 @@ +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration +from footprint.client.configuration.fixture import GlobalConfigFixture +from footprint.client.configuration.default.default_mixin import DefaultMixin +from footprint.main.lib.functions import map_dict + +__author__ = 'calthorpe_associates' + + +class DefaultGlobalConfigFixture(DefaultMixin, GlobalConfigFixture): + + def feature_class_lookup(self): + return {} + + def default_remote_db_entity_configurations(self): + googles = dict( + aerial="https://mt{S}.google.com/vt/lyrs=y&x={X}&y={Y}&z={Z}", + labels="https://mts{S}.google.com/vt/lyrs=h@218000000&hl=en&src=app&x={X}&y={Y}&z={Z}", + map="https://mts{S}.google.com/vt/lyrs=m@219202286,transit:comp%7Cvm:1&hl=en&src=app&opts=r&x={X}&y={Y}&z={Z}&s=G", + ) + google_setups = map_dict( + lambda key, url: dict( + key='google_%s' % key, + url=url, + hosts=["1", "2", "3"], + no_feature_class_configuration=True), + googles) + + cloudmade_setups = [dict( + key='cloudmade_default', + name='Cloudmade - Open Street Maps', + url="http://{S}tile.cloudmade.com/9c5e79c1284c4bbb838bc6d860d84921/998/256/{Z}/{X}/{Y}.png", + hosts=["a.", "b.", "c.", ""], + no_feature_class_configuration=True)] + return google_setups + cloudmade_setups + + def default_db_entity_configurations(self): + config_entity = self.config_entity + remote_db_entity_configurations = self.default_remote_db_entity_configurations() + return map( + lambda remote_db_entity_configuration: create_db_entity_configuration(config_entity, **remote_db_entity_configuration), + remote_db_entity_configurations) + + def import_db_entity_configurations(self, **kwargs): + return [] diff --git a/footprint/client/configuration/default/config_entity/default_project.py b/footprint/client/configuration/default/config_entity/default_project.py new file mode 100644 index 000000000..910753e57 --- /dev/null +++ b/footprint/client/configuration/default/config_entity/default_project.py @@ -0,0 +1,127 @@ +from footprint.main.models.base.base_demographic_feature import BaseDemographicFeature +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.client.configuration.fixture import ProjectFixture, RegionFixture +from footprint.client.configuration.default.default_mixin import DefaultMixin +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.lib.functions import merge +from footprint.main.models import CensusBlockgroup, CensusBlock, BaseFeature, DevelopableFeature +from footprint.main.models.analysis.vmt_features.vmt_trip_lengths_feature import VmtTripLengthsFeature +from footprint.main.models.base.census_tract import CensusTract +from footprint.main.models.base.cpad_holdings_feature import CpadHoldingsFeature +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.utils import get_property_path + +__author__ = 'calthorpe_associates' + + +class DefaultProjectFixture(DefaultMixin, ProjectFixture): + + def feature_class_lookup(self): + # Get the client region fixture (or the default region if the former doesn't exist) + client_region = resolve_fixture("config_entity", "region", RegionFixture) + region_class_lookup = client_region.feature_class_lookup() + return merge( + region_class_lookup, + FeatureClassCreator.db_entity_key_to_feature_class_lookup(self.config_entity, self.default_db_entity_configurations()) + ) + + def default_db_entity_configurations(self, **kwargs): + """ + Projects define DbEntities specific to the project, namely the Base table. To retrieve the subclass of a + certain table, if it defines a base class here, use self.get_db_entity_class(Keys.KEY). + kwargs: overrides can be supplied to override certain values. The override behavior must be hand-crafted + below + :return: a dictionary of + """ + config_entity = self.config_entity + + return [ + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_BASE_FEATURE, + # If a name override is supplied, put it in. Otherwise leave null to derive it from the key + name=get_property_path(kwargs, 'overrides.%s.name' % Keys.DB_ABSTRACT_BASE_FEATURE), + # The Base Feature is normally considered a primary_geography unless overridden + primary_geography=get_property_path(kwargs, 'overrides.%s.primary_geography' % Keys.DB_ABSTRACT_BASE_FEATURE) or True, + primary_key='geography_id', + primary_key_type='varchar', + + # The Base Feature is normally associated to a subclass of Geography unless overridden + geography_class_name=get_property_path(kwargs, 'overrides.%s.geography_class_name' % Keys.DB_ABSTRACT_BASE_FEATURE) or + 'footprint.main.models.geographies.parcel.Parcel', + extent_authority=True, + base_class=BaseFeature, + related_fields=dict(built_form=dict( + single=True, + related_class_name='footprint.main.models.built_form.built_form.BuiltForm', + related_class_join_field_name='key', + source_class_join_field_name='built_form_key')) + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_CENSUS_TRACT, + base_class=CensusTract, + intersection=dict(type='polygon', to='centroid') + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_CENSUS_BLOCKGROUP, + base_class=CensusBlockgroup, + intersection=dict(type='polygon', to='centroid'), + related_fields=dict(census_tract=dict( + single=True, + related_db_entity_key=Keys.DB_ABSTRACT_CENSUS_TRACT, + related_class_join_field_name='tract', + source_class_join_field_name='tract')) + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_CENSUS_BLOCK, + base_class=CensusBlock, + intersection=dict(type='polygon', to='centroid'), + fields=CensusBlock.dynamic_fields(), + related_fields=dict(census_blockgroup=dict( + single=True, + related_db_entity_key=Keys.DB_ABSTRACT_CENSUS_BLOCKGROUP, + related_class_join_field_name='blockgroup', + source_class_join_field_name='blockgroup')) + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_DEVELOPABLE, + base_class=DevelopableFeature, + intersection=dict(type='attribute', db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE), + primary_key='geography_id', + primary_key_type='varchar' + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_CPAD_HOLDINGS_FEATURE, + name='CPAD Holdings', + base_class=CpadHoldingsFeature, + intersection=dict(type='polygon') + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_BASE_DEMOGRAPHIC_FEATURE, + base_class=BaseDemographicFeature, + intersection=dict(type='attribute', db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE), + primary_key='geography_id', + primary_key_type='varchar' + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_VMT_FUTURE_TRIP_LENGTHS_FEATURE, + base_class=VmtTripLengthsFeature, + intersection=dict(type='polygon', to='centroid') + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_VMT_BASE_TRIP_LENGTHS_FEATURE, + base_class=VmtTripLengthsFeature, + intersection=dict(type='polygon', to='centroid') + ) + ] + + def import_db_entity_configurations(self, **kwargs): + return [] diff --git a/footprint/client/configuration/default/config_entity/default_region.py b/footprint/client/configuration/default/config_entity/default_region.py new file mode 100644 index 000000000..6c78225bc --- /dev/null +++ b/footprint/client/configuration/default/config_entity/default_region.py @@ -0,0 +1,26 @@ +from footprint.client.configuration.fixture import RegionFixture, GlobalConfigFixture +from footprint.client.configuration.default.default_mixin import DefaultMixin +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.lib.functions import merge + +__author__ = 'calthorpe_associates' + + +class DefaultRegionFixture(DefaultMixin, RegionFixture): + + def feature_class_lookup(self): + # Get the client global_config fixture (or the default region if the former doesn't exist) + client_global_config = resolve_fixture("config_entity", "global_config", GlobalConfigFixture) + global_config_feature_class_lookup = client_global_config.feature_class_lookup() + return merge(global_config_feature_class_lookup, {}) + + def default_db_entity_configurations(self): + """ + Region define DbEntities specific to the region, + :return: a dictionary of + """ + remote_db_entity_setups = self.default_remote_db_entity_configurations() + return remote_db_entity_setups + + def import_db_entity_configurations(self, **kwargs): + return [] diff --git a/footprint/client/configuration/default/config_entity/default_scenario.py b/footprint/client/configuration/default/config_entity/default_scenario.py new file mode 100644 index 000000000..ca559d1ca --- /dev/null +++ b/footprint/client/configuration/default/config_entity/default_scenario.py @@ -0,0 +1,148 @@ +from django.contrib.auth.models import User +from footprint import settings +from footprint.main.models.analysis.fiscal_feature import FiscalFeature +from footprint.main.models.future.core_end_state_demographic_feature import CoreEndStateDemographicFeature +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.client.configuration.fixture import ScenarioFixture, project_specific_project_fixtures +from footprint.client.configuration.default.default_mixin import DefaultMixin +from footprint.main.lib.functions import merge +from footprint.main.models import FutureScenarioFeature, CoreIncrementFeature, CoreEndStateFeature, \ + VmtQuarterMileBufferFeature, VmtOneMileBufferFeature, VmtVariableBufferFeature, VmtFeature +from footprint.main.models.config.scenario import FutureScenario, Scenario +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +class DefaultScenarioFixture(DefaultMixin, ScenarioFixture): + def feature_class_lookup(self): + # Get the client project fixture (or the default region if the former doesn't exist) + project_class_lookup = merge(*map(lambda project_fixture: project_fixture.feature_class_lookup(), + project_specific_project_fixtures(config_entity=self.config_entity))) + return merge( + project_class_lookup, + FeatureClassCreator.db_entity_key_to_feature_class_lookup(self.config_entity, self.default_db_entity_configurations()) + ) + + def default_db_entity_configurations(self): + """ + Scenarios define DbEntities specific to the Scenario. Creates a list a dictionary of configuration functionality. Some DbEntities have associated base classes for + which a dynamic model subclass is created. Some of these subclasses also have associated fields which + refer to other dynamically created subclasses + :return: + """ + config_entity = self.config_entity + + return map(lambda configuration: create_db_entity_configuration(config_entity, **configuration), + self.matching_scope([ + dict( + class_scope=FutureScenario, + key=Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE, + base_class=FutureScenarioFeature, + import_from_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + import_ids_only=True, + related_fields=dict(built_form=dict( + single=True, + related_class_name='footprint.main.models.built_form.built_form.BuiltForm' + )) + ), + dict( + class_scope=FutureScenario, + key=Keys.DB_ABSTRACT_INCREMENT_FEATURE, + base_class=CoreIncrementFeature, + import_from_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + import_ids_only=True + ), + dict( + class_scope=FutureScenario, + key=Keys.DB_ABSTRACT_END_STATE_FEATURE, + base_class=CoreEndStateFeature, + import_from_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + related_fields=dict(built_form=dict( + single=True, + related_class_name='footprint.main.models.built_form.built_form.BuiltForm' + )) + ), + dict( + class_scope=FutureScenario, + key=Keys.DB_ABSTRACT_END_STATE_DEMOGRAPHIC_FEATURE, + base_class=CoreEndStateDemographicFeature, + import_from_db_entity_key=Keys.DB_ABSTRACT_BASE_DEMOGRAPHIC_FEATURE + ), + dict( + class_scope=FutureScenario, + key=Keys.DB_ABSTRACT_FISCAL_FEATURE, + base_class=FiscalFeature, + import_from_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + import_ids_only=True + ), + dict( + class_scope=Scenario, + key=Keys.DB_ABSTRACT_VMT_QUARTER_MILE_BUFFER_FEATURE, + base_class=VmtQuarterMileBufferFeature, + import_from_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + import_ids_only=True + ), + dict( + class_scope=Scenario, + key=Keys.DB_ABSTRACT_VMT_ONE_MILE_BUFFER_FEATURE, + base_class=VmtOneMileBufferFeature, + import_from_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + import_ids_only=True + ), + dict( + class_scope=Scenario, + key=Keys.DB_ABSTRACT_VMT_VARIABLE_BUFFER_FEATURE, + base_class=VmtVariableBufferFeature, + import_from_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + import_ids_only=True + ), + dict( + class_scope=Scenario, + key=Keys.DB_ABSTRACT_VMT_FEATURE, + base_class=VmtFeature, + import_from_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + import_ids_only=True + ) + ], + class_scope=self.config_entity and self.config_entity.__class__)) + + def import_db_entity_configurations(self, **kwargs): + """ + This is to simulate layer upload + """ + config_entity = self.config_entity + + def new_db_entity_configuration(config_entity, **configuration): + db_entity_configuration = create_db_entity_configuration(config_entity, **configuration) + # Wipe this out so to simulate an upload condition + db_entity_configuration['feature_class_configuration'] = None + return db_entity_configuration + + return map(lambda configuration: new_db_entity_configuration(config_entity, **configuration), + self.matching_scope([ + # dict( + # class_scope=FutureScenario, + # name='Import Test', + # key='import_test', + # url='file://%s/samples/feature_sample.json' % settings.STATIC_ROOT + # ), + dict( + class_scope=FutureScenario, + name='TAZ Import Test', + key='taz_import_test', + url='file://%s/TAZ07_w_tahoe.zip' % settings.STATIC_ROOT, + creator=User.objects.all()[0], + srid=102642, + ), + dict( + class_scope=FutureScenario, + name='California Import Test', + key='california_import_test', + url='file://%s/Sutter_Cities.zip' % settings.STATIC_ROOT, + creator=User.objects.all()[0], + srid=102642, + ) + ], + class_scope=self.config_entity and self.config_entity.__class__)) diff --git a/footprint/client/configuration/default/default_init.py b/footprint/client/configuration/default/default_init.py new file mode 100644 index 000000000..e040a46df --- /dev/null +++ b/footprint/client/configuration/default/default_init.py @@ -0,0 +1,17 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.client.configuration.fixture import InitFixture +from footprint.client.configuration.default.default_mixin import DefaultMixin + +class DefaultInitFixture(DefaultMixin, InitFixture): + def model_class_modules(self): + return [] diff --git a/footprint/client/configuration/default/default_mixin.py b/footprint/client/configuration/default/default_mixin.py new file mode 100644 index 000000000..949bf5652 --- /dev/null +++ b/footprint/client/configuration/default/default_mixin.py @@ -0,0 +1,6 @@ +__author__ = 'calthorpe' + + +class DefaultMixin(object): + def get_default_instance(self, module_name, module_fragment): + return None diff --git a/footprint/client/configuration/default/policy/__init__.py b/footprint/client/configuration/default/policy/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/default/policy/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/default/policy/default_policy.py b/footprint/client/configuration/default/policy/default_policy.py new file mode 100644 index 000000000..6b3591547 --- /dev/null +++ b/footprint/client/configuration/default/policy/default_policy.py @@ -0,0 +1,90 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.client.configuration.fixture import PolicyConfigurationFixture +from footprint.client.configuration.default.default_mixin import DefaultMixin +__author__ = 'calthorpe_associates' + + +class DefaultPolicyConfigurationFixture(DefaultMixin, PolicyConfigurationFixture): + + def policy_sets(self): + return [ + { + 'name': 'Default', + 'key': 'default', + 'description': 'Default policy set', + 'policies': [ + { + 'name': 'Fiscal', + 'key': 'fiscal', + 'description': 'Fiscal policy assumptions', + 'policies': + [ + {'key': 'operations_maintenance_costs', 'name': + 'Annual Fiscal Operations and Maintenance Cost Assumptions', 'policies': + [{'key': 'urban', 'name': 'Urban Operations and Maintenance Cost Assumptions', + 'values': {'single_family_large_lot': 220, 'single_family_small_lot': 220, + 'single_family_attached': 190, 'multifamily': 164}}, + {'key': 'compact_refill', 'name': 'Compact Refill Operations and Maintenance Cost Assumptions', + 'values': {'single_family_large_lot': 213, 'single_family_small_lot': 213, + 'single_family_attached': 187, 'multifamily': 160}}, + {'key': 'compact_greenfield', 'name': 'Compact Greenfield Operations and Maintenance Cost Assumptions', + 'values': {'single_family_large_lot': 223, 'single_family_small_lot': 223, + 'single_family_attached': 196, 'multifamily': 167}}, + {'key': 'standard', 'name': 'Standard Operations and Maintenance Cost Assumptions', + 'values': {'single_family_large_lot': 249, 'single_family_small_lot': 249, + 'single_family_attached': 218, 'multifamily': 186}}] + }, + + {'key': 'revenue', 'name': + 'Revenue Assumptions', 'policies': + [{'key': 'urban', 'name': 'Urban Revenue', + 'values': {'single_family_large_lot': 7299, 'single_family_small_lot': 5763, + 'single_family_attached': 4936, 'multifamily': 3528}}, + {'key': 'compact_refill', 'name': 'Compact Refill Revenue Assumptions', + 'values': {'single_family_large_lot': 7881, 'single_family_small_lot': 6216, + 'single_family_attached': 5319, 'multifamily': 3799}}, + {'key': 'compact_greenfield', 'name': 'Compact Greenfield Revenue Assumptions', + 'values': {'single_family_large_lot': 6768, 'single_family_small_lot': 5349, + 'single_family_attached': 4593, 'multifamily': 3276}}, + {'key': 'standard', 'name': 'Standard Revenue Assumptions', + 'values': {'single_family_large_lot': 5253, 'single_family_small_lot': 4160, + 'single_family_attached': 3575, 'multifamily': 2573}}] + }, + + {'key': 'capital_costs', 'name': + 'Capital Cost Assumptions', 'policies': + [{'key': 'urban', 'name': 'Urban Capital Cost Assumptions', + 'values': {'single_family_large_lot': 5711, 'single_family_small_lot': 5711, + 'single_family_attached': 5711, 'multifamily': 4587}}, + {'key': 'compact_refill', 'name': 'Compact Refill Capital Cost Assumptions', + 'values': {'single_family_large_lot': 5541, 'single_family_small_lot': 4868, + 'single_family_attached': 5016, 'multifamily': 4518}}, + {'key': 'compact_greenfield', 'name': 'Compact Greenfield Capital Cost Assumptions', + 'values': {'single_family_large_lot': 9600, 'single_family_small_lot': 9244, + 'single_family_attached': 9127, 'multifamily': 7379}}, + {'key': 'standard', 'name': 'Standard Capital Cost Assumptions', + 'values': {'single_family_large_lot': 14102, 'single_family_small_lot': 13356, + 'single_family_attached': 12740, 'multifamily': 11044}}] + } + + ] + } + ] + } + ] diff --git a/footprint/client/configuration/default/publishing/__init__.py b/footprint/client/configuration/default/publishing/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/default/publishing/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/default/publishing/default_layer.py b/footprint/client/configuration/default/publishing/default_layer.py new file mode 100644 index 000000000..17982fbcc --- /dev/null +++ b/footprint/client/configuration/default/publishing/default_layer.py @@ -0,0 +1,335 @@ +from footprint.client.configuration.fixture import LayerConfigurationFixture, RegionFixture, GlobalConfigFixture, project_specific_project_fixtures, project_specific_scenario_fixtures +from footprint.client.configuration.default.default_mixin import DefaultMixin +from footprint.main.publishing.layer_initialization import LayerLibraryKey, LayerMediumKey, LayerSort, LayerTag +from footprint.main.publishing.tilestache_style_configuration import create_style_template, create_template_context_dict_for_parent_model +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.lib.functions import flat_map, flatten, unique, merge +from footprint.main.models import BuiltForm, Scenario, Medium, LayerSelection +from footprint.main.models.config.scenario import FutureScenario, BaseScenario +from footprint.main.models.geospatial.feature import Feature +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.presentation_configuration import LayerConfiguration, PresentationConfiguration, ConfigurationData +from footprint.main.models.tag import Tag +from footprint import settings + +__author__ = 'calthorpe_associates' + + +class DefaultLayerConfigurationFixtures(DefaultMixin, LayerConfigurationFixture): + def layer_libraries(self, layers=None): + """ + Returns a PresentationConfiguration for each LayerLibrary scoped to self.config_entity. + The instance will be saved to the LayerLibrary as a foreign key, so it is persisted at that time. + Each instance contains LayerConfiguration instances that match it's config_entity class scope + :return: + """ + # Just make a Scenario scoped configuration + return [ + PresentationConfiguration( + # The default LayerLibrary configuration for all scenarios + scope=self.config_entity.schema(), + key=LayerLibraryKey.DEFAULT, + name='{0} Default Library', # format with config_entity name + description='The default layer library for {0}', # format with config_entity name + data=ConfigurationData( + presentation_media_configurations=self.matching_scope(layers or self.layers(), + class_scope=self.config_entity and self.config_entity.__class__) + ) + ) + ] + + def all_remote_db_entity_setups(self): + # Manually coalesce all possible db_entity setups for remote db_entities. + return unique( + flat_map(lambda client_instance: client_instance.default_remote_db_entity_configurations(), + flatten([ + [resolve_fixture("config_entity", "global_config", GlobalConfigFixture, settings.CLIENT)], + # Assume only one region matching the CLIENT string + [resolve_fixture("config_entity", "region", RegionFixture, settings.CLIENT)], + # Iterate through all project-specific project configs + project_specific_project_fixtures(), + # Iterate through all project-specific scenario configs + project_specific_scenario_fixtures() + ])), + lambda db_entity_setup: db_entity_setup['key']) + + def background_layers(self): + """ + Background layers are simple references to their corresponding db_entities. For now we assume any + db_entity that has a url is a background layer. + :return: + """ + # Find all unique db_entity setups that have the url property + db_entity_setups = filter( + lambda db_entity_setup: db_entity_setup.get('url', None), + self.all_remote_db_entity_setups()) + return map( + lambda db_entity_setup: LayerConfiguration( + scope=Scenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=db_entity_setup['key'], + visible=db_entity_setup['key']=='google_map', + tags=[Tag.objects.get(tag=LayerTag.BACKGROUND_IMAGERY)], + sort_priority=LayerSort.BACKGROUND), + db_entity_setups) + + def layers(self): + """ + Returns LayerConfigurations that are scoped to a certain LayerLibrary key + :return: + """ + + return self.matching_scope(self.background_layers() + [ + # The following layers are Used by both BaseScenario and FutureScenario + LayerConfiguration( + scope=FutureScenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_DEVELOPABLE, + visible=False, + visible_attributes=['developable_index'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict={'attributes': {'developable_index': {'unstyled': True}}}, + sort_priority=LayerSort.BASE + ), + LayerConfiguration( + scope=Scenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_CENSUS_TRACT, + visible=False, + # TODO why style tract codes? + visible_attributes=['tract'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict={'attributes': {'tract': {'unstyled': True}}}, + sort_priority=LayerSort.BASE + ), + LayerConfiguration( + scope=Scenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_CENSUS_BLOCKGROUP, + visible=False, #Temp + visible_attributes=['blockgroup'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + # TODO why style blockgroup codes? + template_context_dict={'attributes': {'blockgroup': {'unstyled': True}}}, + sort_priority=LayerSort.BASE + ), + LayerConfiguration( + scope=Scenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_CENSUS_BLOCK, + visible=False, + visible_attributes=['block'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + # TODO why style block codes? + template_context_dict={'attributes': {'block': {'unstyled': True}}}, + sort_priority=LayerSort.BASE + ), + LayerConfiguration( + scope=Scenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_CPAD_HOLDINGS_FEATURE, + visible=False, + visible_attributes=['wkb_geometry'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}}, + sort_priority=LayerSort.BASE + ), + LayerConfiguration( + scope=Scenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + visible=False, + visible_attributes=['built_form__id'], + column_alias_lookup=dict(built_form__id='built_form_id'), + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict=built_form_template_context_dict(), + sort_priority=LayerSort.BASE + ), + + # The following are only used by FutureScenario + LayerConfiguration( + scope=FutureScenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE, + visible=True, + visible_attributes=['built_form__id'], + # The SQL column returned is normally builform_id, so alias it to our expected attribute string + column_alias_lookup=dict(built_form__id='built_form_id'), + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict=built_form_template_context_dict(), + sort_priority=LayerSort.FUTURE+1 + ), + LayerConfiguration( + scope=FutureScenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_INCREMENT_FEATURE, + visible=False, + visible_attributes=['land_development_category'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict={'attributes': {'land_development_category': {'unstyled': True}}}, + sort_priority=LayerSort.FUTURE+3 + ), + + LayerConfiguration( + scope=FutureScenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_END_STATE_FEATURE, + visible=False, + visible_attributes=['built_form__id'], + column_alias_lookup=dict(built_form__id='built_form_id'), + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict=built_form_template_context_dict(), + sort_priority=LayerSort.FUTURE+2 + ), + + LayerConfiguration( + scope=Scenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_VMT_FEATURE, + visible=False, + visible_attributes=['vmt_daily_per_hh'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict={'attributes': {'vmt_daily_per_hh': {'unstyled': True}}}, + sort_priority=LayerSort.FUTURE+4 + ) + ], class_scope=self.config_entity and self.config_entity.__class__) + + def import_layer_configurations(self): + """ + Generic LayerConfigurations for db_entity layers imported into the system. + """ + return self.matching_scope([ + LayerConfiguration( + scope=FutureScenario.__name__, + # Use Feature for the template key so we match the default Feature + # style files + style_class=Feature, + layer_library_key=LayerLibraryKey.DEFAULT, + visible=True, + visible_attributes=['wkb_geometry'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}}, + sort_priority=LayerSort.FUTURE, + # This is meant to be replaced by the imported db_entity_key + # It serves to uniquely identify the LayerConfiguration + # so that it can be overridden by client configurations + db_entity_key='default_future_scenario_layer_configuration' + ), + LayerConfiguration( + scope=BaseScenario.__name__, + # Use Feature for the template key so we match the default Feature + # style files + style_class=Feature, + layer_library_key=LayerLibraryKey.DEFAULT, + visible=True, + visible_attributes=['wkb_geometry'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}}, + sort_priority=LayerSort.BASE, + # This is meant to be replaced by the imported db_entity_key + # It serves to uniquely identify the LayerConfiguration + # so that it can be overridden by client configurations + db_entity_key='default_base_scenario_layer_configuration' + ) + ], class_scope=self.config_entity and self.config_entity.__class__) + + def update_or_create_media(self): + """ + Iterates through the LayerConfiguration and creates Template instances for each that contain default + styling for the configured attributes + :return: + """ + + # Use the client_scenario to get all the DbEntity key to FeatureClass mappings, since the Scenario will adopt + # from its ancestors + # Find any project_specific scenario_fixtures + project_specific_fixtures = project_specific_project_fixtures() + # Merge feature_class_lookups from each project-specific scenario fixture + feature_class_lookup = merge( + *map( + lambda client_scenario: client_scenario.feature_class_lookup(), + project_specific_scenario_fixtures())) + # Combine unique layer configurations from each project-specific scenario fixture + # Also add in import layer configurations to support imported layers + def layer_configurations_for_client_scenario(client_scenario): + client_fixture = resolve_fixture( + "publishing", + "layer", + LayerConfigurationFixture, + client_scenario.schema) + return client_fixture.layers() + client_fixture.import_layer_configurations() + + # Get a flat unique list of layer configurations for all client scenario fixtures + layer_configurations = unique( + flat_map( + layer_configurations_for_client_scenario, + project_specific_fixtures), + lambda layer_configuration: layer_configuration.db_entity_key) + + # Create a default Medium for any layer that doesn't need a Template + Medium.objects.update_or_create(key=LayerMediumKey.DEFAULT) + + for layer_configuration in layer_configurations: + if layer_configuration.style_class or feature_class_lookup.get(layer_configuration.db_entity_key): + create_style_template( + layer_configuration.template_context_dict, + layer_configuration.db_entity_key, + # The template key is based on the configured feature class or the DbEntity + # If no feature_class exists the db_entity_key is used + # Some layer configurations use styled_class to force a certain class upon + # which to base their template names (namely the generic layer_configurations) + # used for importing new feature tables + layer_configuration.style_class or feature_class_lookup.get(layer_configuration.db_entity_key), + *layer_configuration.visible_attributes) + + # Creates the Template for LayerSelection instances + styled_class = LayerSelection + # This is a magic attribute of tilestache indicating the features that match a query + # TODO we don't style layer selections with cartocss. + # Is this being used on the front end with css/polymaps? + styled_attribute = 'selected' + default_context = { + 'htmlClass': None, + 'attributes': { + styled_attribute: { + 'equals': { + 'TRUE': {"fill": {"color": 'yellow', "opacity": .8}, + "outline": {"color": "red"}, }, + }, + } + } + } + create_style_template(default_context, None, styled_class, styled_attribute) + + +def built_form_template_context_dict(): + """ + Create the template_context_dict based on the built_form_id attribute + :return: + """ + + return create_template_context_dict_for_parent_model( + BuiltForm, + lambda built_form: built_form.medium.content if built_form.medium else None, + 'built_form') + + +def developable_template_context_dict(): + """ + Create the template_context_dict for the DevelopableFeature class + TODO fill this out + :return: + """ + return dict() + + +def core_increment_template_context_dict(): + """ + Initialize the Template for the CoreIncrementFeatures class attributes. + This creates a context of default feature value ranges and colors for each attribute that are copied to the + PresentationMedium.medium_context for each Scenario map presentation. The user can then update the colors + and values in the UI + :return: + """ + return dict() + diff --git a/footprint/client/configuration/default/publishing/default_result.py b/footprint/client/configuration/default/publishing/default_result.py new file mode 100644 index 000000000..2da4bc45c --- /dev/null +++ b/footprint/client/configuration/default/publishing/default_result.py @@ -0,0 +1,384 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.client.configuration.fixture import ResultConfigurationFixture +from footprint.client.configuration.default.default_mixin import DefaultMixin +from footprint.main.publishing.result_initialization import ResultConfiguration, ResultLibraryConfiguration, ResultLibraryKey, ResultKey, ResultMediumKey, ResultSort +from footprint.main.models import Scenario, Medium +from footprint.main.models.config.scenario import BaseScenario, FutureScenario +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.utils import create_media_subdir + + +class DefaultResultConfigurationFixtures(DefaultMixin, ResultConfigurationFixture): + + def result_libraries(self): + """ + Used to update or create ResultLibraries per ConfigEntity instance + Returns the result library(ies) scoped for the class of self.config_entity + :return: + """ + return self.matching_scope([ + ResultLibraryConfiguration( + class_scope=Scenario, + key=ResultLibraryKey.DEFAULT, + name='{0} Default Result Library', + description='The default result library for {0}' + )], + class_scope=self.config_entity and self.config_entity.__class__ + ) + + def results(self): + """ + Used to update or create Results per ConfigEntity instance + Returns the result library(ies) scoped for the class of self.config_entity. + The Result will belong to the ResultLibrary specified by result_library_key + :return: + """ + return self.matching_scope([ + # Basic Core result query that summarizes increments + + ResultConfiguration( + class_scope=BaseScenario, + result_type='bar_graph', + result_library_key=ResultLibraryKey.DEFAULT, + result_db_entity_key=ResultKey.BASE_EMPLOYMENT_BY_TYPE, + source_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + + name='Base: Employment by Sector', + attributes=['retail', 'office', 'industrial', 'public', 'other'], + db_column_lookup=dict( + retail='emp_ret', + office='emp_off', + industrial='emp_ind', + public='emp_public_admin', + other='emp_military', + ), + labels=['Retail', 'Office', 'Industrial', 'Public', 'Other'], + stackable=True, + is_stacked=False, + create_query=lambda result_config: 'SELECT SUM(emp_ret) as emp_ret__sum, SUM(case when emp_off - emp_public_admin < 0 then 0 else emp_off - emp_public_admin end) as emp_off__sum, SUM(emp_ind + emp_ag) as emp_ind__sum, SUM(emp_public_admin) as emp_public_admin__sum, SUM(emp_military) as emp_military__sum FROM %({0})s'.format(Keys.DB_ABSTRACT_BASE_FEATURE), + sort_priority=ResultSort.BASE + ), + + ResultConfiguration( + class_scope=BaseScenario, + result_type='bar_graph', + result_library_key=ResultLibraryKey.DEFAULT, + result_db_entity_key=ResultKey.BASE_DWELLING_UNITS_BY_TYPE, + source_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + + name='Base: Dwelling Units by Type', + attributes=['single_family', 'attached', 'multifamily'], + db_column_lookup=dict( + single_family='du_detsf', + attached='du_attsf', + multifamily='du_mf' + ), + labels=['Single Family', 'Attached', 'Multifamily'], + stackable=True, + is_stacked=False, + create_query=self.simple_aggregate, + sort_priority=ResultSort.BASE + ), + + ResultConfiguration( + class_scope=FutureScenario, + result_library_key=ResultLibraryKey.DEFAULT, + result_type='bar_graph', + result_db_entity_key=ResultKey.INCREMENTS, + source_db_entity_key=Keys.DB_ABSTRACT_INCREMENT_FEATURE, + + name='Increments: All Metrics', + attributes=['population', 'dwelling_units', 'employment'], + db_column_lookup=dict( + population='pop', + dwelling_units='du', + employment='emp' + ), + extent_lookup=dict( + population=dict(min=-25000, max=25000), + dwelling_units=dict(min=-2500, max=2500), + employment=dict(min=-2500, max=2500), + ), + labels=['Population', 'Dwelling Units', 'Employment'], + stackable=False, + is_stacked=False, + create_query=self.simple_aggregate, + sort_priority=ResultSort.FUTURE + ), + + # DB Entity Core result query that summarizes dwellings by type + ResultConfiguration( + class_scope=FutureScenario, + result_library_key=ResultLibraryKey.DEFAULT, + result_type='bar_graph', + result_db_entity_key=ResultKey.INCREMENTS_DWELLING_UNITS_BY_TYPE, + source_db_entity_key=Keys.DB_ABSTRACT_INCREMENT_FEATURE, + + name='Increments: Dwelling Units by Type', + attributes=['single_family_large_lot', 'single_family_small_lot', 'multi_family', 'attached_single_family'], + db_column_lookup=dict( + single_family_large_lot='du_detsf_ll', + single_family_small_lot='du_detsf_sl', + multi_family='du_mf', + attached_single_family='du_attsf', + ), + labels=['SF Large Lot', 'SF Small Lot', 'MF', 'Attached SF'], + stackable=False, + is_stacked=False, + create_query=self.simple_aggregate, + sort_priority=ResultSort.FUTURE + ), + ResultConfiguration( + class_scope=FutureScenario, + result_library_key=ResultLibraryKey.DEFAULT, + result_type='bar_graph', + result_db_entity_key=ResultKey.END_STATE_DWELLING_UNITS_BY_TYPE, + source_db_entity_key=Keys.DB_ABSTRACT_END_STATE_FEATURE, + + name='End State: Dwelling Units by Type', + attributes=['single_family_large_lot', 'single_family_small_lot', 'multi_family', 'attached_single_family'], + db_column_lookup=dict( + single_family_large_lot='du_detsf_ll', + single_family_small_lot='du_detsf_sl', + multi_family='du_mf', + attached_single_family='du_attsf', + ), + labels=['SF Large Lot', 'SF Small Lot', 'MF', 'Attached SF'], + stackable=True, + is_stacked=False, + show_attrs_as_percents=True, + create_query=self.simple_aggregate, + sort_priority=ResultSort.FUTURE + ), + ResultConfiguration( + class_scope=FutureScenario, + result_library_key=ResultLibraryKey.DEFAULT, + result_type='bar_graph', + result_db_entity_key=ResultKey.INCREMENTS_EMPLOYMENT_BY_TYPE, + source_db_entity_key=Keys.DB_ABSTRACT_INCREMENT_FEATURE, + + name='Increments: Employment by Sector', + attributes=['retail', 'office', 'industrial', 'public', 'other'], + db_column_lookup=dict( + retail='emp_ret', + office='emp_off', + industrial='emp_ind', + public='emp_public_admin', + other='emp_military', + ), + labels=['Retail', 'Office', 'Industrial', 'Public', 'Other'], + stackable=False, + is_stacked=False, + create_query=lambda result_config: 'SELECT SUM(emp_ret) as emp_ret__sum, SUM(case when emp_off - emp_public_admin < 0 then 0 else emp_off - emp_public_admin end) as emp_off__sum, SUM(emp_ind + emp_ag) as emp_ind__sum, SUM(emp_public_admin) as emp_public_admin__sum, SUM(emp_military) as emp_military__sum FROM %({0})s'.format(Keys.DB_ABSTRACT_INCREMENT_FEATURE), + sort_priority=ResultSort.FUTURE + ), + ResultConfiguration( + class_scope=FutureScenario, + result_library_key=ResultLibraryKey.DEFAULT, + result_type='bar_graph', + result_db_entity_key=ResultKey.END_STATE_EMPLOYMENT_BY_TYPE, + source_db_entity_key=Keys.DB_ABSTRACT_END_STATE_FEATURE, + + name='End State: Employment by Sector', + attributes=['retail', 'office', 'industrial', 'public', 'other'], + db_column_lookup=dict( + retail='emp_ret', + office='emp_off', + industrial='emp_ind', + public='emp_public_admin', + other='emp_military', + ), + labels=['Retail', 'Office', 'Industrial', 'Public', 'Other'], + stackable=True, + is_stacked=False, + create_query=lambda result_config: 'SELECT SUM(emp_ret) as emp_ret__sum, SUM(case when emp_off - emp_public_admin < 0 then 0 else emp_off - emp_public_admin end) as emp_off__sum, SUM(emp_ind + emp_ag) as emp_ind__sum, SUM(emp_public_admin) as emp_public_admin__sum, SUM(emp_military) as emp_military__sum FROM %({0})s'.format(Keys.DB_ABSTRACT_END_STATE_FEATURE), + sort_priority=ResultSort.FUTURE + ), + + ResultConfiguration( + class_scope=FutureScenario, + result_library_key=ResultLibraryKey.DEFAULT, + result_type='analytic_bars', + result_db_entity_key=ResultKey.INCREMENTS_BARS, + source_db_entity_key=Keys.DB_ABSTRACT_END_STATE_FEATURE, + + name='Results Increments', + attributes=['population', 'dwelling_units', 'employment'], + db_column_lookup=dict( + population='pop', + dwelling_units='du', + employment='emp' + ), + extent_lookup=dict( + population=dict(min=0, max=5000000), + dwelling_units=dict(min=0, max=1000000), + employment=dict(min=0, max=1000000) + ), + labels=['Population', 'Dwelling Units', 'Employment'], + stackable=False, + is_stacked=False, + create_query=self.simple_aggregate, + sort_priority=ResultSort.FUTURE + ), + ResultConfiguration( + class_scope=FutureScenario, + result_library_key=ResultLibraryKey.DEFAULT, + result_type='analytic_result', + result_db_entity_key=ResultKey.FISCAL, + source_db_entity_key=Keys.DB_ABSTRACT_FISCAL_FEATURE, + + name='Fiscal', + attributes=['capital_costs', 'operations_maintenance', 'revenue'], + db_column_lookup=dict( + capital_costs='residential_capital_costs', + operations_maintenance='residential_operations_maintenance_costs', + revenue='residential_revenue' + ), + labels=['capital_costs', 'operations_maintenance', 'revenue'], + stackable=False, + is_stacked=False, + create_query=self.simple_aggregate, + sort_priority=ResultSort.FUTURE + ), + ResultConfiguration( + class_scope=Scenario, + result_type='analytic_result', + result_library_key=ResultLibraryKey.DEFAULT, + result_db_entity_key=ResultKey.VMT, + source_db_entity_key=Keys.DB_ABSTRACT_VMT_FEATURE, + + name='VMT', + attributes=['annual_vmt', 'daily_vmt', 'daily_vmt_per_hh', 'annual_vmt_per_capita'], + db_column_lookup=dict( + annual_vmt='vmt_annual_w_trucks', + daily_vmt='vmt_daily_w_trucks', + daily_vmt_per_hh='vmt_daily_per_hh', + annual_vmt_per_capita='vmt_annual_per_capita' + ), + labels=['Annual VMT', 'Daily VMT', 'Daily VMT Per HH', 'Annual VMT Per Capita'], + stackable=False, + is_stacked=False, + create_query=lambda result_config: 'SELECT SUM(vmt_annual_w_trucks) as annual_vmt__sum, SUM(vmt_daily_w_trucks) as daily_vmt__sum, Case when SUM(hh) > 0 then SUM(final_prod_hbo + final_prod_hbw + final_prod_nhb) * 2 / SUM(hh) else 0 end as daily_vmt_per_hh__sum, Case when SUM(pop) > 0 then SUM(final_prod_hbo + final_prod_hbw + final_prod_nhb) * 350 * 2 / SUM(pop) else 0 end as annual_vmt_per_capita__sum, Case when SUM(emp) > 0 then SUM(final_attr_hbo + final_attr_hbw + final_attr_nhb) * 2 / SUM(emp) else 0 end as daily_vmt_per_emp__sum FROM %({0})s'.format(Keys.DB_ABSTRACT_VMT_FEATURE), + sort_priority=ResultSort.FUTURE + ) + ], class_scope=self.config_entity and self.config_entity.__class__) + + def update_or_create_media(self): + """ + :return: Creates a Media used by the default results + """ + + # Make sure the styles directory exists so that Result css can be stored there + # TODO do we need to store styles in the filesystem. Or can they just be in the DB?? + create_media_subdir('styles') + + # Used by any Result that has no explicit Medium + Medium.objects.update_or_create( + key=ResultMediumKey.DEFAULT, + name=ResultMediumKey.DEFAULT, + defaults={ + 'content_type': Keys.CONTENT_TYPE_PYTHON, + 'content': { + 'colorRange': ['yellow', 'purple'] + }, + 'description': 'Default'} + ) + + Medium.objects.update_or_create( + key=ResultMediumKey.BASE_EMPLOYMENT_BY_TYPE, + name=ResultMediumKey.BASE_EMPLOYMENT_BY_TYPE, + defaults={ + 'content_type': Keys.CONTENT_TYPE_PYTHON, + 'content': { + 'colorRange': ['purple', 'pink'] + }, + 'description': 'Base: Employment by Type'} + ), + + Medium.objects.update_or_create( + key=ResultMediumKey.BASE_DWELLING_UNITS_BY_TYPE, + name=ResultMediumKey.BASE_DWELLING_UNITS_BY_TYPE, + defaults={ + 'content_type': Keys.CONTENT_TYPE_PYTHON, + 'content': { + 'colorRange': ['yellow', 'red'] + }, + 'description': 'Base: Dwelling Units by Type'} + ), + + Medium.objects.update_or_create( + key=ResultMediumKey.INCREMENTS, + name=ResultMediumKey.INCREMENTS, + defaults={ + 'content_type': Keys.CONTENT_TYPE_PYTHON, + 'content': { + 'colorRange': ['yellow', 'purple'] + }, + 'description': 'Increments'} + ) + + Medium.objects.update_or_create( + key=ResultMediumKey.INCREMENTS_EMPLOYMENT_BY_TYPE, + name=ResultMediumKey.INCREMENTS_EMPLOYMENT_BY_TYPE, + defaults={ + 'content_type': Keys.CONTENT_TYPE_PYTHON, + 'content': { + 'colorRange': ['purple', 'pink'] + }, + 'description': 'Increments: Employment by Type'} + ), + + Medium.objects.update_or_create( + key=ResultMediumKey.INCREMENTS_DWELLING_UNITS_BY_TYPE, + name=ResultMediumKey.INCREMENTS_DWELLING_UNITS_BY_TYPE, + defaults={ + 'content_type': Keys.CONTENT_TYPE_PYTHON, + 'content': { + 'colorRange': ['yellow', 'red'] + }, + 'description': 'Increments: Dwelling Units by Type'} + ), + + Medium.objects.update_or_create( + key=ResultMediumKey.END_STATE, + name=ResultMediumKey.END_STATE, + defaults={ + 'content_type': Keys.CONTENT_TYPE_PYTHON, + 'content': { + 'colorRange': ['yellow', 'purple'] + }, + 'description': 'End State'} + ), + + Medium.objects.update_or_create( + key=ResultMediumKey.END_STATE_EMPLOYMENT_BY_TYPE, + name=ResultMediumKey.END_STATE_EMPLOYMENT_BY_TYPE, + defaults={ + 'content_type': Keys.CONTENT_TYPE_PYTHON, + 'content': { + 'colorRange': ['purple', 'pink'] + }, + 'description': 'End State: Employment by Type'} + ), + + Medium.objects.update_or_create( + key=ResultMediumKey.END_STATE_DWELLING_UNITS_BY_TYPE, + name=ResultMediumKey.END_STATE_DWELLING_UNITS_BY_TYPE, + defaults={ + 'content_type': Keys.CONTENT_TYPE_PYTHON, + 'content': { + 'colorRange': ['yellow', 'red'] + }, + 'description': 'End State: Dwelling Units by Type'} + ) diff --git a/footprint/client/configuration/fixture.py b/footprint/client/configuration/fixture.py new file mode 100644 index 000000000..0f1d7bc20 --- /dev/null +++ b/footprint/client/configuration/fixture.py @@ -0,0 +1,451 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +# Hand-crafted basic fixture classes +# This stuff might be replaced by Django's json style fixtures in the future. +from footprint.client.configuration.utils import resolve_parent_fixture, resolve_fixture +from footprint.main.lib.functions import all_existing_keys_match, all_existing_classes_subclass, unique +from footprint.main.utils.utils import expect +from footprint import settings + +class Fixture(object): + def __init__(self, schema, *args, **kwargs): + """ + Creates a fixture instance + :param schema: __-separated string that matches a ConfigEntity schema. This is used to designate what scope + the fixture represents. If None, the Fixture is global/default. If one segment, the fixture represents + a region (as of now equivalent to a settings.CLIENT). If two segments, it's scoped to a project within a + region. If three segments (rare), it's scoped to a Scenario within a project. + :param args: optional args used by the fixture + :param kwargs: optional args used by a fixture. The most common is config_entity, which provides a fixture + with a reference to its scoped config_entity in cases where the latter already exists and the fixture is + provided configuration to that config_entity (e.g. layers) + :return: + """ + super(Fixture, self).__init__() + # The default subclass of the Fixture. This is delegated to when a client-specific fixture calls the super + # version of its methods. That way all default fixtures can be combined with custom client fixtures + self.schema = schema + self.init_args(**kwargs) + self.args = args + self.kwargs = kwargs + self.expect(*self.expect_kwargs()) + + def expect_kwargs(self): + """ + Returns the required kwargs for a subclass. Default is none + :return: An array of attribute strings. + """ + return [] + + # Class scope variables indicating the schema-relative module location of the fixture + module = None, + module_fragment = None + + @property + def base_class(self): + """ + Returns the base class is this class hierarchy that is used for matching the parent_fixture and default_fixture + :return: + """ + return None + + _parent_fixture = None + # A reference to the parent fixture instance of this instance. The parent is the fixture represented by the + # next level up of the schema string. So if self.schema is region__project, the parent is that with schema region. + # If already at the region scope, the parent fixture is the default (global) fixture. + @property + def parent_fixture(self): + self._parent_fixture = self._parent_fixture or \ + resolve_parent_fixture(self.module, self.module_fragment, self.base_class, self.schema, *self.args, **self.kwargs) + return self._parent_fixture + + def init_args(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + + def expect(self, *args): + """ + Pass arg strings that are required by self to validate. + :param args: + :return: + """ + # This is the utils version + expect(self, *args) + + def matching_scope(self, fixtures, **kwargs): + """ + Filters fixtures by given matching_dict. All keys specified in the matching_dict that are present + in the fixture must match using isSubclass, where the matching_dict value must be a subclass of the + fixtrue's value + :param fixtures: A list of dictionaries or objects. + :param kwargs key/values whose values are classes. + :return: + """ + return filter(lambda fixtures: all_existing_classes_subclass(fixtures, **kwargs), fixtures) + + def matching_keys(self, fixtures, **kwargs): + """ + Filters fixtures by given matching_dict_obj. All keys specified in the matching_dict that are present + in the fixture must match + :param fixtures: A list of dictionaries or objects. + :param kwargs: key/values that are used to filter the fixtures + :return: The fixtures that pass the filter + """ + return filter(lambda fixtures: all_existing_keys_match(fixtures, **kwargs), fixtures) + + +class InitFixture(Fixture): + + @property + def base_class(self): + return InitFixture + + def import_database(self): + """ + Optionally configure an import database for the client. The result dict is sent to import_data.ImportData + dict( + host=HOST_DB_IP + database=DB_NAME + user=DB_USER + password=DB_PW) + :return: + """ + return None + + def model_class_modules(self): + """ + Returns an array of tuples with each tuple in the form (module, module_fragment) where module is a string + that indicates a module under initialization.client.[client], such as 'built_form' or 'publishing'. + module_fragment is the name of the sub module without the client name, such as 'tilestache' to indicate + [client]_tilestache under the publishing package + :return: + """ + return [] + + def populate_models(self): + pass + + +class FootprintFixture(Fixture): + def __init__(self, schema, *args, **kwargs): + super(FootprintFixture, self).__init__(schema, *args, **kwargs) + # Expect these kwargs to be supplied + self.expect('config_entity', 'built_form', 'policy') + +class ConfigEntitiesFixture(Fixture): + module = 'config_entity' + module_fragment = 'config_entities' + + @property + def base_class(self): + return ConfigEntitiesFixture + + def regions(self): + return [] + + def projects(self, region=None): + return [] + + def scenarios(self, project=None, class_scope=None): + """ + Returns a definition that should be called with all the projects to create one or more scenarios per + project. + :param project Optional Project to specify to limit the scenarios returned to those whose project_key + matches the project.key and to those who have no project_key + :param class_scope Optional Sceanrio subclass by which to filter the fixtures according to the + fixture's class_scope attribute + :return: A list of scenario fixtures + """ + return [] + + def import_scenarios(self, origin_config_entity): + return self.parent_fixture.import_scenarios(origin_config_entity) + +class BuiltFormFixture(Fixture): + module = 'built_form' + module_fragment = 'built_form' + + def expect_kwargs(self): + return ['config_entity'] + + @property + def base_class(self): + return BuiltFormFixture + + def built_forms(self): + """ + Returns fully instantiated client-specific BuiltForm subclass instances plus the default ones. + :return: A dictionary keyed by the BuiltForm subclass name or similar, valued by the instances + """ + + + def built_form_sets(self): + """ + Returns client specific BuiltFormSets as a dictionary of fixtures + :return: + """ + return [] + + + def built_form_styles(self): + """ + Returns a symbology dict keyed by the name of a client-specific built_form to its color + :return: + """ + + +class PolicyConfigurationFixture(Fixture): + module = 'policy' + module_fragment = 'policy' + + def policy_sets(self): + pass + + @property + def base_class(self): + return PolicyConfigurationFixture + + +class ConfigEntityFixture(Fixture): + """ + Base Class to configure DbEntities at a certain ConfigEntity scope (e.g. Project) + """ + + # The config_entity is required for default_db_entity_configurations, but not for feature_class_lookup + config_entity = None + + def default_remote_db_entity_configurations(self): + # A list of simple dictionaries containing a key, url and optional hosts array, that describe how to + # configure a remote db_entity. + return [] + + def default_db_entity_configurations(self, **kwargs): + """ + Creates client-specific db_entity_and_class configurations + kwargs - overrides is a dict used to override values in the config. For instance: + dict(Keys.DB_ABSTRACT_FEATURE=dict(name='Foo')) could be used to override the name of a DbEntitySetup. + The usage of the overrides is up to the receiving method + :return: + """ + return self.parent_fixture.default_db_entity_configurations(**kwargs) + + def import_db_entity_configurations(self, **kwargs): + return self.parent_fixture.import_db_entity_configurations(**kwargs) + + def feature_class_lookup(self): + """ + A ConfigEntity-independent lookup of DbEntity keys to Feature classes. The same information is + provided by default_db_entity_configurations but the latter is ConfigEntity instance-specific + :return: + """ + return dict() + + def non_default_owned_db_entities(self): + """ + Return all DbEntities that are owned by this config_entity but not from this fixture, meaning they were created by the user + or some kind of import process. + """ + default_db_entity_configurations = self.default_db_entity_configurations() + default_db_entity_keys = map(lambda db_entity_configuration: db_entity_configuration['key'], default_db_entity_configurations) + additional_db_entity_keys = set(map(lambda db_entity: db_entity.key, self.config_entity.owned_db_entities())) - set(default_db_entity_keys) + return self.config_entity.computed_db_entities(key__in=additional_db_entity_keys) + + @classmethod + def resolve_config_entity_fixture(cls, config_entity): + from footprint.main.models.config.scenario import Scenario + from footprint.main.models.config.project import Project + from footprint.main.models.config.region import Region + from footprint.main.models.config.global_config import GlobalConfig + if isinstance(config_entity, Scenario): + module_fragment, subclass = ("scenario", ScenarioFixture) + elif isinstance(config_entity, Project): + module_fragment, subclass = ("project", ProjectFixture) + elif isinstance(config_entity, Region): + module_fragment, subclass = ("region", RegionFixture) + elif isinstance(config_entity, GlobalConfig): + module_fragment, subclass = ("global_config", GlobalConfigFixture) + return resolve_fixture( + "config_entity", + module_fragment, + subclass, + config_entity.schema(), + config_entity=config_entity) + +class GlobalConfigFixture(ConfigEntityFixture): + module = 'config_entity' + module_fragment = 'global_config' + + @property + def base_class(self): + return GlobalConfigFixture + + +class RegionFixture(ConfigEntityFixture): + module = 'config_entity' + module_fragment = 'region' + + @property + def base_class(self): + return RegionFixture + +class ProjectFixture(ConfigEntityFixture): + module = 'config_entity' + module_fragment = 'project' + + @property + def base_class(self): + return ProjectFixture + +class ScenarioFixture(ConfigEntityFixture): + module = 'config_entity' + module_fragment = 'scenario' + + @property + def base_class(self): + return ScenarioFixture + +class PresentationConfigurationFixture(Fixture): + # Some calls will require putting a config_entity in scope via init, but not all + config_entity = None + + def update_or_create_media(self): + """ + Initializes the Presentation specific Media instances. Override this for client-specific media + """ + self.parent_fixture.update_or_create_media() + +class LayerConfigurationFixture(PresentationConfigurationFixture): + module = 'publishing' + module_fragment = 'layer' + + @property + def base_class(self): + return LayerConfigurationFixture + + def layer_libraries(self, layers=None): + """ + Creates a PresentationConfiguration instance used to configure a LayerLibrary for a certain ConfigEntity + class scope + :param layers override the layers + :return: + """ + pass + + def layers(self): + """ + A list of LayerConfiguration instances that configure visible layers based on db_entities. This + function should merge in the result of background_layers. + :return: An array of LayerConfigurations with the background_layers merged in + """ + return [] + + def background_layers(self): + """ + A subset of layers used as background layers. These are also LayerConfigurations + :return: An Array of LayerConfigurations + """ + return [] + + def import_layer_configurations(self): + """ + Generic LayerConfigurations (no db_enity_key) that are used for the layers of imported + db_entities/features tables. + """ + return [] + +class LandUseSymbologyFixture(Fixture): + module='publishing' + module_fragment='land_use_symbology' + + @property + def base_class(self): + return LandUseSymbologyFixture + + def land_use_color_lookup(self): + """ + Maps an attribute of a client-specific class to a color. + :return: + """ + return dict() + +class ResultConfigurationFixture(PresentationConfigurationFixture): + """ + Fixtures related to result presentations, such as ResultLibrary and Result instances + """ + module='publishing' + module_fragment='result' + + @property + def base_class(self): + return ResultConfigurationFixture + + def result_libraries(self): + """ + Returns the ResultLibrary configurationsscoped for the class of self.config_entity + :return: + """ + return self.parent_fixture.result_libraries() + + def results(self): + """ + Returns ResultConfiguration instances. These configure a Result that is created for each config_entity + whose scope matches or inherits that the ResultConfiguration.class_scope + :return: + """ + return self.parent_fixture.results() + + @staticmethod + def simple_aggregate(result_config): + return [ + # self.aggregate(Sum('column1'), ...) + ('aggregate', map( + lambda attribute: dict(Sum=result_config.db_column_lookup[attribute]), + result_config.attributes)) + ] + +# This is more of a literal fixture. +class MediumFixture(Fixture): + def __init__(self, *args, **kwargs): + super(MediumFixture, self).__init__('global', *args, **kwargs) + + def expect_kwargs(self): + return ['key', 'name'] + +def project_specific_project_fixtures(**kwargs): + """ + Convenience method to find ProjectFixture instances for all projects of the client. + :kwargs: Optional arguments for result_fixture, such as the config_entity + :return: + """ + return unique(map( + lambda schema: resolve_fixture("config_entity", "project", ProjectFixture, schema, **kwargs), + project_schemas_of_client()), lambda fixture: fixture.__class__) + +def project_specific_scenario_fixtures(**kwargs): + """ + Convenience method to find ScenarioFixture instances for all projects of the client. + :kwargs: Optional arguments for result_fixture, such as the config_entity + :return: + """ + return unique(map( + lambda schema: resolve_fixture("config_entity", "scenario", ScenarioFixture, schema, **kwargs), + project_schemas_of_client()), lambda fixture: fixture.__class__) + +def project_schemas_of_client(**kwargs): + """ + Extract all the schemas under of the settings.CLIENT in [client]_config_entities.project + :kwargs: Optional arguments for result_fixture, such as the config_entity + :return: + """ + client_config_entities = resolve_fixture("config_entity", "config_entities", ConfigEntitiesFixture, settings.CLIENT, **kwargs) + return map(lambda project_config: '%s__%s' % (client_config_entities.schema, project_config['key']), client_config_entities.projects()) diff --git a/footprint/client/configuration/mixins/__init__.py b/footprint/client/configuration/mixins/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/mixins/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/mixins/publishing/__init__.py b/footprint/client/configuration/mixins/publishing/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/mixins/publishing/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/mixins/publishing/layer_primary_base.py b/footprint/client/configuration/mixins/publishing/layer_primary_base.py new file mode 100644 index 000000000..965324a23 --- /dev/null +++ b/footprint/client/configuration/mixins/publishing/layer_primary_base.py @@ -0,0 +1,24 @@ +from footprint.client.configuration.fixture import LandUseSymbologyFixture +from footprint.main.publishing.tilestache_style_configuration import create_template_context_dict_for_related_field +from footprint.client.configuration.utils import resolve_fixture +from footprint import settings + +__author__ = 'calthorpe_associates' + + +def primary_base_template_context_dict(client_land_use_definition_class): + # Resolve the client's specific color lookup for the PrimaryParcelFeature land_use_definition + client_symbology = resolve_fixture( + "publishing", "land_use_symbology", LandUseSymbologyFixture, settings.CLIENT) + + if not client_symbology: + return + + color_lookup = client_symbology.land_use_color_lookup() + + # Create a default TemplateContext dict. This context will style the foreign key attribute of the LandUseDefinition + return create_template_context_dict_for_related_field( + 'land_use_definition__id', + client_land_use_definition_class, + color_lookup, + 'land_use') diff --git a/footprint/client/configuration/sacog/__init__.py b/footprint/client/configuration/sacog/__init__.py new file mode 100644 index 000000000..1490f6205 --- /dev/null +++ b/footprint/client/configuration/sacog/__init__.py @@ -0,0 +1,30 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.client.configuration.sacog.base.sacog_existing_land_use_parcel_feature \ + import SacogExistingLandUseParcelFeature +from footprint.client.configuration.sacog.base.sacog_hardwood_feature import SacogHardwoodFeature +from footprint.client.configuration.sacog.base.sacog_stream_feature import SacogStreamFeature +from footprint.client.configuration.sacog.base.sacog_vernal_pool_feature import SacogVernalPoolFeature +from footprint.client.configuration.sacog.base.sacog_wetland_feature import SacogWetlandFeature +# Explicitly list model classes so that they are by South +from footprint.client.configuration.sacog.built_form.sacog_land_use import SacogLandUse +from footprint.client.configuration.sacog.built_form.sacog_land_use_definition import SacogLandUseDefinition + +from footprint.client.configuration.sacog.base.sacog_light_rail_feature import SacogLightRailFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_feature import SacogLightRailStopsFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_one_mile_feature \ + import SacogLightRailStopsOneMileFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_half_mile_feature \ + import SacogLightRailStopsHalfMileFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_quarter_mile_feature \ + import SacogLightRailStopsQuarterMileFeature diff --git a/footprint/client/configuration/sacog/base/__init__.py b/footprint/client/configuration/sacog/base/__init__.py new file mode 100644 index 000000000..221c91da7 --- /dev/null +++ b/footprint/client/configuration/sacog/base/__init__.py @@ -0,0 +1,21 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +__author__ = 'calthorpe_associates' + diff --git a/footprint/client/configuration/sacog/base/sacog_existing_land_use_parcel_feature.py b/footprint/client/configuration/sacog/base/sacog_existing_land_use_parcel_feature.py new file mode 100644 index 000000000..0971212dc --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_existing_land_use_parcel_feature.py @@ -0,0 +1,47 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.base.primary_parcel_feature import PrimaryParcelFeature + +__author__ = 'calthorpe_associates' + + +class SacogExistingLandUseParcelFeature(PrimaryParcelFeature): + + census_blockgroup = models.CharField(max_length=100, null=True, blank=True) + census_block= models.CharField(max_length=100, null=True, blank=True) + land_use = models.CharField(max_length=100, null=True, blank=True) + acres = models.DecimalField(max_digits=14, decimal_places=4) + du = models.DecimalField(max_digits=14, decimal_places=4) + jurisdiction= models.CharField(max_length=100, null=True, blank=True) + notes = models.CharField(max_length=100, null=True, blank=True) + emp = models.DecimalField(max_digits=14, decimal_places=4) + ret = models.DecimalField(max_digits=14, decimal_places=4) + off = models.DecimalField(max_digits=14, decimal_places=4) + pub = models.DecimalField(max_digits=14, decimal_places=4) + ind = models.DecimalField(max_digits=14, decimal_places=4) + other = models.DecimalField(max_digits=14, decimal_places=4) + assessor= models.CharField(max_length=100, null=True, blank=True) + gp = models.CharField(max_length=100, null=True, blank=True) + gluc = models.CharField(max_length=100, null=True, blank=True) + + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/client/configuration/sacog/base/sacog_hardwood_feature.py b/footprint/client/configuration/sacog/base/sacog_hardwood_feature.py new file mode 100644 index 000000000..55ef9eb05 --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_hardwood_feature.py @@ -0,0 +1,32 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class SacogHardwoodFeature(Feature): + area = models.DecimalField(max_digits=14, decimal_places=2) + perimeter = models.DecimalField(max_digits=10, decimal_places=2) + hardwood_code = models.CharField(max_length=20, null=True, blank=True) + + class Meta(object): + abstract = True + app_label = 'main' \ No newline at end of file diff --git a/footprint/client/configuration/sacog/base/sacog_light_rail_feature.py b/footprint/client/configuration/sacog/base/sacog_light_rail_feature.py new file mode 100644 index 000000000..7d8ec64d3 --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_light_rail_feature.py @@ -0,0 +1,33 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class SacogLightRailFeature(Feature): + length = models.DecimalField(max_digits=14, decimal_places=2) + status = models.DecimalField(max_digits=10, decimal_places=2) + line = models.CharField(max_length=20, null=True, blank=True) + + class Meta(object): + abstract = True + app_label = 'main' + + diff --git a/footprint/client/configuration/sacog/base/sacog_light_rail_stops_feature.py b/footprint/client/configuration/sacog/base/sacog_light_rail_stops_feature.py new file mode 100644 index 000000000..491cf8f13 --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_light_rail_stops_feature.py @@ -0,0 +1,35 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class SacogLightRailStopsFeature(Feature): + name = models.CharField(max_length=20, null=True, blank=True) + node = models.IntegerField(null=True) + color = models.IntegerField(null=True) + stop_name = models.CharField(max_length=40, null=True, blank=True) + active = models.CharField(max_length=20, null=True, blank=True) + + class Meta(object): + abstract = True + app_label = 'main' + + diff --git a/footprint/client/configuration/sacog/base/sacog_light_rail_stops_half_mile_feature.py b/footprint/client/configuration/sacog/base/sacog_light_rail_stops_half_mile_feature.py new file mode 100644 index 000000000..6b6f9662b --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_light_rail_stops_half_mile_feature.py @@ -0,0 +1,30 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class SacogLightRailStopsHalfMileFeature(Feature): + + class Meta(object): + abstract = True + app_label = 'main' + + diff --git a/footprint/client/configuration/sacog/base/sacog_light_rail_stops_one_mile_feature.py b/footprint/client/configuration/sacog/base/sacog_light_rail_stops_one_mile_feature.py new file mode 100644 index 000000000..518a76366 --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_light_rail_stops_one_mile_feature.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class SacogLightRailStopsOneMileFeature(Feature): + + + class Meta(object): + abstract = True + app_label = 'main' + + diff --git a/footprint/client/configuration/sacog/base/sacog_light_rail_stops_quarter_mile_feature.py b/footprint/client/configuration/sacog/base/sacog_light_rail_stops_quarter_mile_feature.py new file mode 100644 index 000000000..439332bf6 --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_light_rail_stops_quarter_mile_feature.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class SacogLightRailStopsQuarterMileFeature(Feature): + + + class Meta(object): + abstract = True + app_label = 'main' + + diff --git a/footprint/client/configuration/sacog/base/sacog_stream_feature.py b/footprint/client/configuration/sacog/base/sacog_stream_feature.py new file mode 100644 index 000000000..35b889cde --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_stream_feature.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class SacogStreamFeature(Feature): + area = models.DecimalField(max_digits=14, decimal_places=2) + perimeter = models.DecimalField(max_digits=10, decimal_places=2) + stream_code = models.CharField(max_length=20, null=True, blank=True) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/client/configuration/sacog/base/sacog_vernal_pool_feature.py b/footprint/client/configuration/sacog/base/sacog_vernal_pool_feature.py new file mode 100644 index 000000000..1d11fa8ea --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_vernal_pool_feature.py @@ -0,0 +1,31 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class SacogVernalPoolFeature(Feature): + area = models.DecimalField(max_digits=14, decimal_places=2) + perimeter = models.DecimalField(max_digits=14, decimal_places=2) + vernal_pool_code = models.CharField(max_length=20, null=True, blank=True) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/client/configuration/sacog/base/sacog_wetland_feature.py b/footprint/client/configuration/sacog/base/sacog_wetland_feature.py new file mode 100644 index 000000000..4e84d0945 --- /dev/null +++ b/footprint/client/configuration/sacog/base/sacog_wetland_feature.py @@ -0,0 +1,30 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class SacogWetlandFeature(Feature): + area = models.DecimalField(max_digits=14, decimal_places=2) + perimeter = models.DecimalField(max_digits=10, decimal_places=2) + wetland_code = models.CharField(max_length=20, null=True, blank=True) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/client/configuration/sacog/built_form/__init__.py b/footprint/client/configuration/sacog/built_form/__init__.py new file mode 100644 index 000000000..f27f3a1bb --- /dev/null +++ b/footprint/client/configuration/sacog/built_form/__init__.py @@ -0,0 +1,12 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + diff --git a/footprint/client/configuration/sacog/built_form/import_csv/__init__.py b/footprint/client/configuration/sacog/built_form/import_csv/__init__.py new file mode 100644 index 000000000..f27f3a1bb --- /dev/null +++ b/footprint/client/configuration/sacog/built_form/import_csv/__init__.py @@ -0,0 +1,12 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + diff --git a/footprint/client/configuration/sacog/built_form/import_csv/buildings.csv b/footprint/client/configuration/sacog/built_form/import_csv/buildings.csv new file mode 100644 index 000000000..43512d8c7 --- /dev/null +++ b/footprint/client/configuration/sacog/built_form/import_csv/buildings.csv @@ -0,0 +1 @@ +id,source,hyperlink,building_type,building,vacancy_rate,household_size,all_uses,du_density,emp_density,Pct_SF_Large_Lot,Pct_SF_Small_Lot,Pct_Attached_SF,Pct_MF_2_to_4,Pct_MF_5_Plus,Pct_Emp_Office_Svc,Pct_Educ_Svc,Pct_Medical_Svc,Pct_Public_Admin,Pct_Retail_Svc,Pct_Restaurant,Pct_Accommodation,Pct_Arts_Entertainment,Pct_Other_Svc,Pct_Manufacturing,Pct_Transport_warehouse,Pct_Wholesale,Pct_Construction_Util,Pct_Agriculture,Pct_Extraction,percent_of_building_type,floors,total_far,parking_spaces,parking_structure_square_feet,residential_efficiency,residential_lot_square_feet,square_feet_per_du,retail_efficiency,retail_square_feet_per_employee,office_efficiency,office_square_feet_per_employee,industrial_efficiency,industrial_square_feet_per_employee,misc_percent_of_retail_use,retail_percent_of_retail_use,restaurant_percent_of_retail_use,grocery_percent_of_retail_use,hardscape_percent,irrigated_percent,impervious_roof_percent,impervious_hardscape_percent,pervious_hardscape_percent,softscape_landscape_percent,id 35,SACOG 2016 MTPSCS,,Agricultural Processing/Retail Employment,Agricultural Processing/Retail Employment,0,0,,0,0,0,0,0,0,0,0.1,0,0,0,0.05,0,0,0,0,0.5,0.35,0,0,0,0,1,1,0.056,12,0,0,0,0,0.85,650,0.85,420,0.85,1300,0,0,0,0,0,0,0,0,0,0,60035 43,SACOG 2016 MTPSCS,,Agriculture (SACOG),Agriculture (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0.01,0,0,0,0,0,0,0,0,0,1,1200,0,0,0,0,0,0,0,0,0,0,60043 36,SACOG 2016 MTPSCS,,Airport (SACOG),Airport (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0.05,0.1,0.1,0,0,0,0.2,0.55,0,0,0,0,1,1,0.06,15,0,0,0,0,0.85,650,0.85,450,0.85,1550,0,0,0,0,0,0,0,0,0,0,60036 44,SACOG 2016 MTPSCS,,Blank Place Type,Blank Place Type,0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60044 23,SACOG 2016 MTPSCS,,CBD Office,CBD Office,0,0,,0,0,0,0,0,0,0,0.77,0,0,0.18,0.02,0.02,0,0,0.01,0,0,0,0,0,0,1,20,6,200,0,0,0,0,0.85,550,0.85,305.5,0,0,0,0,0,0,0,0,0,0,0,0,60023 30,SACOG 2016 MTPSCS,,Civic/Institution (SACOG),Civic/Institution (SACOG),0,0,,0,0,0,0,0,0,0,0.95,0,0,0.05,0,0,0,0,0,0,0,0,0,0,0,1,4,0.32,40,0,0,0,0,0,0,0.85,592.29,0,0,0,0,0,0,0,0,0,0,0,0,60030 29,SACOG 2016 MTPSCS,,College/University (SACOG),College/University (SACOG),0,0,,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1.5,325,0,0,0,0,0,0,0.85,555.37,0,0,0,0,0,0,0,0,0,0,0,0,60029 16,SACOG 2016 MTPSCS,,Community/Neighborhood Commercial,Community/Neighborhood Commercial,0,0,,0,0,0,0,0,0,0,0.25,0,0,0,0.55,0.1,0,0.05,0.05,0,0,0,0,0,0,1,2,0.4,64,0,0,0,0,0.85,617,0.85,370,0,0,0,0,0,0,0,0,0,0,0,0,60016 17,SACOG 2016 MTPSCS,,Community/Neighborhood Commercial/Office,Community/Neighborhood Commercial/Office,0,0,,0,0,0,0,0,0,0,0.5,0,0,0,0.4,0.1,0,0,0,0,0,0,0,0,0,1,2,0.45,50,0,0,0,0,0.85,650,0.85,344.45,0,0,0,0,0,0,0,0,0,0,0,0,60017 15,SACOG 2016 MTPSCS,,Community/Neighborhood Retail,Community/Neighborhood Retail,0,0,,0,0,0,0,0,0,0,0,0,0,0,0.7,0.18,0,0.02,0.1,0,0,0,0,0,0,1,2,0.3,67,0,0,0,0,0.85,462.8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60015 2,SACOG 2016 MTPSCS,,Farm Home,Farm Home,0.031,2.81,,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,1.6,0,1,43560,3000,0,0,0,0,0,0,0,0,0,0,0.04,0.05,0,0,0,0,60002 46,SACOG 2016 MTPSCS,,Forest (SACOG),Forest (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60046 26,SACOG 2016 MTPSCS,,Heavy Industrial,Heavy Industrial,0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.85,0.15,0,0,0,0,1,1,0.35,20,0,0,0,0,0.85,550,0.85,370,0.85,1178,0,0,0,0,0,0,0,0,0,0,60026 10,SACOG 2016 MTPSCS,,High Density Attached Residential,High Density Attached Residential,0.0635,2.07,,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0.9183,80,0,1,0,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60010 22,SACOG 2016 MTPSCS,,High-Intensity Office,High-Intensity Office,0,0,,0,0,0,0,0,0,0,0.67,0,0,0.38,0.03,0.02,0,0,0,0,0,0,0,0,0,1,10,2.2,35,0,0,0,0,0.85,540,0.85,298.575,0,0,0,0,0,0,0,0,0,0,0,0,60022 20,SACOG 2016 MTPSCS,,Hotel (SACOG),Hotel (SACOG),0,0,,0,0,0,0,0,0,0,0.05,0,0,0,0,0,0.95,0,0,0,0,0,0,0,0,1,12,5,125,0,0,0,0,0.85,1407.1,0.85,370,0,0,0,0,0,0,0,0,0,0,0,0,60020 28,SACOG 2016 MTPSCS,,K-12 School (SACOG),K-12 School (SACOG),0,0,,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0.3,65,0,0,0,0,0,0,0.85,555.27,0,0,0,0,0,0,0,0,0,0,0,0,60028 45,SACOG 2016 MTPSCS,,Large Lot Detached (Not Farm Home),Large Lot Detached (Not Farm Home),0.031,2.81,,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,1.6,0,1,43560,2500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60045 25,SACOG 2016 MTPSCS,,Light Industrial,Light Industrial,0,0,,0,0,0,0,0,0,0,0.2,0,0,0,0.03,0.02,0,0,0,0.25,0.25,0.25,0,0,0,1,1,0.45,30,0,0,0,0,0.85,550,0.85,280.39,0.85,918,0,0,0,0,0,0,0,0,0,0,60025 24,SACOG 2016 MTPSCS,,Light Industrial/Office,Light Industrial/Office,0,0,,0,0,0,0,0,0,0,0.72,0,0,0,0.02,0.01,0,0,0,0.1,0.1,0.05,0,0,0,1,3,0.5,85,0,0,0,0,0.85,580,0.85,346.9,0.85,1000,0,0,0,0,0,0,0,0,0,0,60024 4,SACOG 2016 MTPSCS,,Low Density Detached Residential,Low Density Detached Residential,0.031,2.81,,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,17,0,1,5445,2800,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60004 47,SACOG 2016 MTPSCS,,Low Intensity Office (SACOG),Low Intensity Office (SACOG),0,0,,0,0,0,0,0,0,0,0.98,0,0,0,0.1,0.1,0,0,0,0,0,0,0,0,0,1,2,0.28,60,0,0,0,0,0.85,580,0.85,384.5,0,0,0,0,0,0,0,0,0,0,0,0,60047 27,SACOG 2016 MTPSCS,,Medical Facility,Medical Facility,0,0,,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,9,2.1,175,0,0,0,0,0,0,0.85,441.78,0,0,0,0,0,0,0,0,0,0,0,0,60027 7,SACOG 2016 MTPSCS,,Medium Density Attached Residential,Medium Density Attached Residential,0.0411,2.26,,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0.440774,22,0,1,0,1600,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60007 5,SACOG 2016 MTPSCS,,Medium Density Detached Residential,Medium Density Detached Residential,0.031,2.86,,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,20,0,1,3630,2500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60005 8,SACOG 2016 MTPSCS,,Medium-High Density Attached Residential,Medium-High Density Attached Residential,0.0411,2.26,,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1.02445,40,0,1,0,1785,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60008 6,SACOG 2016 MTPSCS,,Medium-High Density Detached Residential,Medium-High Density Detached Residential,0.031,2.86,,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,40,0,1,1742.4,1500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60006 37,SACOG 2016 MTPSCS,,Military (SACOG),Military (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0.1,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60037 9,SACOG 2016 MTPSCS,,Mobile Home Park,Mobile Home Park,0.031,2.81,,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,40,0,1,1742.4,1500,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60009 21,SACOG 2016 MTPSCS,,Moderate-Intensity Office,Moderate-Intensity Office,0,0,,0,0,0,0,0,0,0,0.85,0,0,0.1,0.03,0.02,0,0,0,0,0,0,0,0,0,1,5,1.5,92,0,0,0,0,0.85,550,0.85,301.58,0,0,0,0,0,0,0,0,0,0,0,0,60021 38,SACOG 2016 MTPSCS,,Park and/or Open Space (SACOG),Park and/or Open Space (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60038 32,SACOG 2016 MTPSCS,,Parking Lot (SACOG),Parking Lot (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60032 34,SACOG 2016 MTPSCS,,Parking Structure (SACOG),Parking Structure (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,5,4.5,600,0,0,0,0,0.008,650,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60034 33,SACOG 2016 MTPSCS,,Parking Structure w/Ground Floor Retail (SACOG),Parking Structure w/Ground Floor Retail (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0,0.35,0.35,0,0.15,0.15,0,0,0,0,0,0,1,5,4.5,500,0,0,0,0,0.11,525.9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60033 31,SACOG 2016 MTPSCS,,Public/Quasi-Public,Public/Quasi-Public,0,0,,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,2,0.31,65,0,0,0,0,0,0,0.85,573.8,0,0,0,0,0,0,0,0,0,0,0,0,60031 19,SACOG 2016 MTPSCS,,Regional Comercial/Office,Regional Comercial/Office,0,0,,0,0,0,0,0,0,0,0.25,0,0,0,0.6,0.1,0,0.05,0,0,0,0,0,0,0,1,2,0.75,78,0,0,0,0,0.85,661.4,0.85,375,0,0,0,0,0,0,0,0,0,0,0,0,60019 18,SACOG 2016 MTPSCS,,Regional Retail,Regional Retail,0,0,,0,0,0,0,0,0,0,0.05,0,0,0,0.7,0.2,0,0,0.05,0,0,0,0,0,0,1,2,0.7,72,0,0,0,0,0.85,505,0.85,304.7,0,0,0,0,0,0,0,0,0,0,0,0,60018 14,SACOG 2016 MTPSCS,,Residential/Retail Mixed Use High,Residential/Retail Mixed Use High,0.0635,1.87,,0,0,0,0,0,0,0.7,0.05,0,0,0,0.1,0.08,0,0.02,0.05,0,0,0,0,0,0,1,4,2,130,0,0.85,0,959.9,0.85,400.7,0.85,250,0,0,0,0,0,0,0,0,0,0,0,0,60014 13,SACOG 2016 MTPSCS,,Residential/Retail Mixed Use Low,Residential/Retail Mixed Use Low,0.0635,1.9,,0,0,0,0,0,0,0.45,0.15,0,0,0,0.1,0.1,0.05,0.05,0.1,0,0,0,0,0,0,1,2,1.0208,20,0,0.85,0,850,0.85,800,0.85,468,0,0,0,0,0,0,0,0,0,0,0,0,60013 40,SACOG 2016 MTPSCS,,Roads (SACOG),Roads (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60040 1,SACOG 2016 MTPSCS,,Rural Residential (SACOG),Rural Residential (SACOG),0.031,3.41,,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,1.6,0,1,43560,2500,0,0,0,0,0,0,0,0,0,0,0.05,0.1,0,0,0,0,60001 42,SACOG 2016 MTPSCS,,UC Davis,UC Davis,0,0,,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,1,325,0,0,0,0,0,0,0.85,578.5,0,0,0,0,0,0,0,0,0,0,0,0,60042 12,SACOG 2016 MTPSCS,,Urban Attached Residential,Urban Attached Residential,0.0635,2.07,,0,0,0,0,0,0,0.95,0,0,0,0,0.02,0.01,0,0.01,0.01,0,0,0,0,0,0,1,15,4,150,0,0.85,0,1125.5,0.85,925.5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60012 39,SACOG 2016 MTPSCS,,Urban Reserve,Urban Reserve,0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60039 11,SACOG 2016 MTPSCS,,Very High Density Attached Residential,Very High Density Attached Residential,0.0635,2.07,,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,4,2.070706,90,0,1,0,1100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60011 3,SACOG 2016 MTPSCS,,Very Low Density Detached Residential,Very Low Density Detached Residential,0.031,2.81,,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,10,0,1,10890,2800,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60003 41,SACOG 2016 MTPSCS,,Water (SACOG),Water (SACOG),0,0,,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60041 \ No newline at end of file diff --git a/footprint/client/configuration/sacog/built_form/import_csv/buildingtypes.csv b/footprint/client/configuration/sacog/built_form/import_csv/buildingtypes.csv new file mode 100644 index 000000000..2755bc1f9 --- /dev/null +++ b/footprint/client/configuration/sacog/built_form/import_csv/buildingtypes.csv @@ -0,0 +1,48 @@ +meta_class,btid,color_hex,building_type +Office/Industrial,1,#948A54,Agricultural Processing/Retail Employment +Agricultural,2,#948A54,Agriculture (SACOG) +Civic,3,#262626,Airport (SACOG) +Blank,4,,Blank Place Type +Office/Industrial,5,#FF3399,CBD Office +Civic,6,#BFBFBF,Civic/Institution (SACOG) +Institutional,7,#595959,College/University (SACOG) +Commercial/Retail,8,#FF0000,Community/Neighborhood Commercial +Commercial/Retail,9,#FF0000,Community/Neighborhood Commercial/Office +Commercial/Retail,10,#FB3300,Community/Neighborhood Retail +Residential,11,#99FF99,Farm Home +Agricultural,12,#948A54,Forest (SACOG) +Office/Industrial,13,#9900CC,Heavy Industrial +Residential,14,#CC6600,High Density Attached Residential +Office/Industrial,15,#FF66CC,High-Intensity Office +Commercial/Retail,16,#963634,Hotel (SACOG) +Civic,17,#808080,K-12 School (SACOG) +Residential,18,#FFFFCC,Large Lot Detached (Not Farm Home) +Office/Industrial,19,#9933FF,Light Industrial +Office/Industrial,20,#CC99FF,Light Industrial/Office +Residential,21,#FFFF00,Low Density Detached Residential +Office/Industrial,22,#FF99CC,Low Intensity Office (SACOG) +Office/Industrial,23,#404040,Medical Facility +Residential,24,#FF9900,Medium Density Attached Residential +Residential,25,#FABF8F,Medium Density Detached Residential +Residential,26,#F27900,Medium-High Density Attached Residential +Residential,27,#FFCC66,Medium-High Density Detached Residential +Institutional,28,,Military (SACOG) +Residential,29,#FF6600,Mobile Home Park +Office/Industrial,30,#FF99CC,Moderate-Intensity Office +Park,31,#92D050,Park and/or Open Space (SACOG) +Commercial/Retail,32,#C4BD97,Parking Lot (SACOG) +Commercial/Retail,33,#948A54,Parking Structure (SACOG) +Commercial/Retail,34,#494529,Parking Structure w/Ground Floor Retail (SACOG) +Civic,35,#D9D9D9,Public/Quasi-Public +Office/Industrial,36,#800000,Regional Comercial/Office +Commercial/Retail,37,#CC0000,Regional Retail +Mixed Use,38,#3399FF,Residential/Retail Mixed Use High +Mixed Use,39,#66CCFF,Residential/Retail Mixed Use Low +Street,40,,Roads (SACOG) +Residential,41,#FFFFCC,Rural Residential (SACOG) +Institutional,42,#595959,UC Davis +Residential,43,#7A2900,Urban Attached Residential +Park,44,,Urban Reserve +Residential,45,#993300,Very High Density Attached Residential +Residential,46,#FFFFCC,Very Low Density Detached Residential +Park,47,,Water (SACOG) diff --git a/footprint/client/configuration/sacog/built_form/import_csv/placetypes.csv b/footprint/client/configuration/sacog/built_form/import_csv/placetypes.csv new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/client/configuration/sacog/built_form/sacog_built_form.py b/footprint/client/configuration/sacog/built_form/sacog_built_form.py new file mode 100644 index 000000000..3881928bc --- /dev/null +++ b/footprint/client/configuration/sacog/built_form/sacog_built_form.py @@ -0,0 +1,93 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.core.management import call_command +from django.template.defaultfilters import slugify + +from footprint.client.configuration.fixture import BuiltFormFixture, LandUseSymbologyFixture +from footprint.client.configuration.sacog.built_form.sacog_land_use_definition import SacogLandUseDefinition +from footprint.client.configuration.sacog.built_form.sacog_land_use import SacogLandUse +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.lib.functions import merge +from footprint.main.models import PlacetypeComponent, PrimaryComponent, Placetype, Region +from footprint.main.models.built_form.built_form import update_or_create_built_form_medium + +from footprint.local_settings import CLIENT +from footprint import settings + + +class SacogBuiltFormFixture(BuiltFormFixture): + def built_forms(self): + def construct_sacog_land_uses(): + + land_use_symbology_fixture = resolve_fixture( + "publishing", + "land_use_symbology", + LandUseSymbologyFixture, + settings.CLIENT) + land_use_lookup = land_use_symbology_fixture.land_use_color_lookup() + + return map( + lambda land_use_definition: SacogLandUse.objects.update_or_create( + key='sac_lu__' + slugify(land_use_definition.land_use).replace('-', ','), + defaults=dict( + name=land_use_definition.land_use, + land_use_definition=land_use_definition, + medium=update_or_create_built_form_medium( + 'sacog_land_use_%s' % land_use_definition.land_use[30:], + land_use_lookup.get(land_use_definition.land_use, None) + ) + ))[0], + SacogLandUseDefinition.objects.all()) + + return merge( + self.parent_fixture.built_forms(client=CLIENT), + self.parent_fixture.built_forms(), + dict(sacog_land_use=construct_sacog_land_uses())) + #TODO determine the utility of this condition : #if isinstance(self.config_entity, Region) else {} + # ) + + def tag_built_forms(self, built_forms_dict): + self.parent_fixture.tag_built_forms(built_forms_dict), + # Give client built_forms a default tag if they don't have any tag yet + # for built_form in built_forms_dict['sacog_land_use']: + # if built_form.tags.count() == 0: + # tag, created, updated = Tag.objects.update_or_create( + # tag=built_form.land_use_definition.land_use or 'Unsorted') + # built_form.tags.add(tag) + + def built_form_sets(self): + return self.parent_fixture.built_form_sets() + self.matching_scope([ + dict( + scope=Region, + key='sacog_buildings', + name='SACOG Buildings', + description='Built Forms for SACOG', + client='sacog', + clazz=PrimaryComponent, + ), + dict( + scope=Region, + key='sacog_buildingtypes', + name='SACOG Buildingtypes', + description='Built Forms for SACOG', + client='sacog', + clazz=PlacetypeComponent, + ), + dict( + scope=Region, + key='sacog_placetypes', + name='SACOG Placetypes', + description='Built Forms for SACOG', + client='sacog', + clazz=Placetype, + ), + ], class_scope=self.config_entity and self.config_entity.__class__) diff --git a/footprint/client/configuration/sacog/built_form/sacog_import_placetype_component.py b/footprint/client/configuration/sacog/built_form/sacog_import_placetype_component.py new file mode 100644 index 000000000..3d3130d3b --- /dev/null +++ b/footprint/client/configuration/sacog/built_form/sacog_import_placetype_component.py @@ -0,0 +1,26 @@ +__author__ = 'calthorpe_associates' + +from csvImporter.fields import CharField, FloatField, IntegerField +from csvImporter.model import CsvModel + +__author__ = 'calthorpe_associates' + +class ImportPlacetypeComponent(CsvModel): +# BTID,Building_Type, +# Urban Mixed Use,Urban Residential,Urban Commercial,City Mixed Use,City Residential,City Commercial, +# Town Mixed Use,Town Residential,Town Commercial,Village Mixed Use,Village Residential,Village Commercial, +# Neighborhood Residential,Neighborhood Low,Office Focus,Mixed Office and R&D,Office/Industrial,Industrial Focus, +# Low-Density Employment Park,High Intensity Activity Center,Mid Intensity Activity Center, +# Low Intensity Retail-Centered N'Hood,Retail: Strip Mall/ Big Box,Industrial/Office/Res Mixed High, +# Industrial/Office/Res Mixed Low,Suburban Multifamily,Suburban Mixed Residential,Residential Subdivision, +# Large Lot Residential Area,Rural Residential,Rural Ranchettes,Rural Employment,Campus/ University, +# Institutional,Parks & Open Space,BuildingType Name,Gross_Net_Flag + category = CharField(prepare=lambda x: x or '') + btid = IntegerField(prepare=lambda x: x or 0) + color = CharField(prepare=lambda x: x or '') + name = CharField(prepare=lambda x: x or '') + + class Meta: + delimiter = "," + has_header = True + diff --git a/footprint/client/configuration/sacog/built_form/sacog_land_use.py b/footprint/client/configuration/sacog/built_form/sacog_land_use.py new file mode 100644 index 000000000..64c45ac8e --- /dev/null +++ b/footprint/client/configuration/sacog/built_form/sacog_land_use.py @@ -0,0 +1,22 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.client.configuration.sacog.built_form.sacog_land_use_definition import SacogLandUseDefinition +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.built_form.client_land_use import ClientLandUse + +class SacogLandUse(ClientLandUse): + objects = GeoInheritanceManager() + land_use_definition = models.ForeignKey(SacogLandUseDefinition, null=False) + + class Meta(object): + app_label = 'main' diff --git a/footprint/client/configuration/sacog/built_form/sacog_land_use_definition.py b/footprint/client/configuration/sacog/built_form/sacog_land_use_definition.py new file mode 100644 index 000000000..d367c4340 --- /dev/null +++ b/footprint/client/configuration/sacog/built_form/sacog_land_use_definition.py @@ -0,0 +1,53 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.built_form.client_land_use_definition import ClientLandUseDefinition + +__author__ = 'calthorpe_associates' + +from django.db import models + +class SacogLandUseDefinition(ClientLandUseDefinition): + objects = GeoInheritanceManager() + + land_use = models.CharField(max_length=100, null=True, blank=True) + min_du_ac = models.DecimalField(max_digits=9, decimal_places=2, default=0) + max_du_ac = models.DecimalField(max_digits=9, decimal_places=2, default=0) + max_emp_ac = models.DecimalField(max_digits=9, decimal_places=2, default=0) + + rural_flag = models.BooleanField(default=False) + detached_flag = models.BooleanField(default=False) + attached_flag = models.BooleanField(default=False) + + pct_ret_rest = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_ret_ret = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_ret_svc = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_off_gov = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_off_off = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_off_svc = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_off_med = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_ind = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_pub_edu = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_pub_med = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_pub_gov = models.DecimalField(max_digits=9, decimal_places=2, default=0) + pct_other = models.DecimalField(max_digits=9, decimal_places=2, default=0) + + class Meta(object): + abstract = False + app_label = 'main' diff --git a/footprint/client/configuration/sacog/built_form/sacog_land_use_definitions.json b/footprint/client/configuration/sacog/built_form/sacog_land_use_definitions.json new file mode 100644 index 000000000..ace37bc88 --- /dev/null +++ b/footprint/client/configuration/sacog/built_form/sacog_land_use_definitions.json @@ -0,0 +1,1227 @@ +[ + { + "pk": 1, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": true, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": true, + "land_use": "Rural Residential", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 1, + "pct_ret_svc": 0 + } + }, + { + "pk": 2, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": true, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": true, + "land_use": "Farm Home", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 1, + "pct_ret_svc": 0 + } + }, + { + "pk": 3, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 1.1, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": true, + "land_use": "Very Low Density Detached Residential", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 4, + "pct_ret_svc": 0 + } + }, + { + "pk": 4, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 4.1, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": true, + "land_use": "Low Density Detached Residential", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 8, + "pct_ret_svc": 0 + } + }, + { + "pk": 5, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 8.1, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": true, + "land_use": "Medium Density Detached Residential", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 12, + "pct_ret_svc": 0 + } + }, + { + "pk": 6, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 12.1, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": true, + "land_use": "Medium-High Density Detached Residential", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 25, + "pct_ret_svc": 0 + } + }, + { + "pk": 7, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 8.1, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Medium Density Attached Residential", + "attached_flag": true, + "pct_off_off": 0, + "max_du_ac": 12, + "pct_ret_svc": 0 + } + }, + { + "pk": 8, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 12.1, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Medium-High Density Attached Residential", + "attached_flag": true, + "pct_off_off": 0, + "max_du_ac": 25, + "pct_ret_svc": 0 + } + }, + { + "pk": 9, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": true, + "land_use": "Mobile Home Park", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 25, + "pct_ret_svc": 0 + } + }, + { + "pk": 10, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 25.1, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "High Density Attached Residential", + "attached_flag": true, + "pct_off_off": 0, + "max_du_ac": 44, + "pct_ret_svc": 0 + } + }, + { + "pk": 11, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 44.1, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Very High Density Attached Residential", + "attached_flag": true, + "pct_off_off": 0, + "max_du_ac": 82, + "pct_ret_svc": 0 + } + }, + { + "pk": 12, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.33, + "pct_ret_rest": 0.33, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 6, + "rural_flag": false, + "min_du_ac": 82.1, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Urban Attached Residential", + "attached_flag": true, + "pct_off_off": 0, + "max_du_ac": 125, + "pct_ret_svc": 0.33 + } + }, + { + "pk": 13, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.156843, + "pct_ret_rest": 0.156843, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 75, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Residential/Retail Mixed Use Low", + "attached_flag": true, + "pct_off_off": 0.529, + "max_du_ac": 26, + "pct_ret_svc": 0.156843 + } + }, + { + "pk": 14, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.208125, + "pct_ret_rest": 0.20625, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 47, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Residential/Retail Mixed Use High", + "attached_flag": true, + "pct_off_off": 0.375, + "max_du_ac": 55, + "pct_ret_svc": 0.208125 + } + }, + { + "pk": 15, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 268, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "High-Rise Mixed Use", + "attached_flag": true, + "pct_off_off": 0, + "max_du_ac": 344, + "pct_ret_svc": 0 + } + }, + { + "pk": 16, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 135, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Mid-Rise Mixed Use", + "attached_flag": true, + "pct_off_off": 0, + "max_du_ac": 198, + "pct_ret_svc": 0 + } + }, + { + "pk": 17, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 37, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Urban Mid-Rise Residential", + "attached_flag": true, + "pct_off_off": 0, + "max_du_ac": 361, + "pct_ret_svc": 0 + } + }, + { + "pk": 18, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.33, + "pct_ret_rest": 0.33, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 20, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Community/Neighborhood Retail", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0.33 + } + }, + { + "pk": 19, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0.444, + "pct_ret_ret": 0.5004, + "pct_ret_rest": 0.0556, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 28, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Community/Neighborhood Commercial", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 20, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0.706, + "pct_ret_ret": 0.2646, + "pct_ret_rest": 0.0294, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 37, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Community/Neighborhood Commercial/Office", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 21, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0.116, + "pct_ret_ret": 0.7072, + "pct_ret_rest": 0.1326, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 53, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Regional Retail", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0.0442 + } + }, + { + "pk": 22, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0.4, + "pct_ret_ret": 0.54, + "pct_ret_rest": 0.06, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 50, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Regional Comercial/Office", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 23, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 150, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Hotel", + "attached_flag": false, + "pct_off_off": 0.144, + "max_du_ac": 0, + "pct_ret_svc": 0.856 + } + }, + { + "pk": 24, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0.44235, + "pct_ret_ret": 0, + "pct_ret_rest": 0.011322, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 180, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0.0983, + "detached_flag": false, + "land_use": "Moderate-Intensity Office", + "attached_flag": false, + "pct_off_off": 0.44235, + "max_du_ac": 0, + "pct_ret_svc": 0.005678 + } + }, + { + "pk": 25, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0.011322, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 294, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0.34405, + "detached_flag": false, + "land_use": "High-Intensity Office", + "attached_flag": false, + "pct_off_off": 0.63895, + "max_du_ac": 0, + "pct_ret_svc": 0.005678 + } + }, + { + "pk": 26, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.005661, + "pct_ret_rest": 0.005661, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 711, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0.14745, + "detached_flag": false, + "land_use": "CBD Office", + "attached_flag": false, + "pct_off_off": 0.83555, + "max_du_ac": 0, + "pct_ret_svc": 0.005661 + } + }, + { + "pk": 27, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.014652, + "pct_ret_rest": 0.014652, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0.105, + "max_emp_ac": 44, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Light Industrial-Office", + "attached_flag": false, + "pct_off_off": 0.85, + "max_du_ac": 0, + "pct_ret_svc": 0.014652 + } + }, + { + "pk": 28, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.041958, + "pct_ret_rest": 0.041958, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0.539, + "max_emp_ac": 27, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Light Industrial", + "attached_flag": false, + "pct_off_off": 0.335, + "max_du_ac": 0, + "pct_ret_svc": 0.041958 + } + }, + { + "pk": 29, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 1, + "max_emp_ac": 11, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Heavy Industrial", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 30, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0.907, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0.093, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 176, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Medical Facility", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 31, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 1, + "pct_ind": 0, + "max_emp_ac": 20, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "K-12 School", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 32, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 1, + "pct_ind": 0, + "max_emp_ac": 100, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Colleges and Universities", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 33, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0.05, + "pct_off_med": 0, + "pct_off_svc": 0.95, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 20, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Civic/Institution", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 34, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 1, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 20, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Public/Quasi-Public", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 35, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Parking Lot", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 36, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.33, + "pct_ret_rest": 0.33, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 41.5, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Parking Structure w/Ground Floor Retail", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0.33 + } + }, + { + "pk": 37, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.33, + "pct_ret_rest": 0.33, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 2.4, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Parking Structure", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0.33 + } + }, + { + "pk": 38, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.269, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0.635, + "max_emp_ac": 7, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Agricultural Processing/Retail Employment", + "attached_flag": false, + "pct_off_off": 0.096, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 39, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0.0516, + "pct_ret_rest": 0.0774, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0.387, + "max_emp_ac": 2, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0.097, + "detached_flag": false, + "land_use": "Airport", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0.387 + } + }, + { + "pk": 40, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0.3, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 1, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Military", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 41, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Park and/or Open Space", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 42, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Urban Reserve", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 43, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Road", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 44, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Water", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 45, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Blank Place Type", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 46, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Forest", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 47, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "Agriculture", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 0, + "pct_ret_svc": 0 + } + }, + { + "pk": 48, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 0, + "pct_ind": 0, + "max_emp_ac": 0, + "rural_flag": true, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": true, + "land_use": "LARGE LOT NOT FARM HOME", + "attached_flag": false, + "pct_off_off": 0, + "max_du_ac": 1, + "pct_ret_svc": 0 + } + }, + { + "pk": 49, + "model": "main.sacoglandusedefinition", + "fields": { + "pct_pub_gov": 0, + "pct_off_med": 0, + "pct_off_svc": 0, + "pct_ret_ret": 0, + "pct_ret_rest": 0, + "pct_pub_med": 0, + "pct_pub_edu": 1, + "pct_ind": 0, + "max_emp_ac": 64, + "rural_flag": false, + "min_du_ac": 0, + "pct_other": 0, + "pct_off_gov": 0, + "detached_flag": false, + "land_use": "UC DAVIS", + "attached_flag": true, + "pct_off_off": 0, + "max_du_ac": 3, + "pct_ret_svc": 0 + } + } +] \ No newline at end of file diff --git a/footprint/client/configuration/sacog/config_entity/__init__.py b/footprint/client/configuration/sacog/config_entity/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/sacog/config_entity/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/sacog/config_entity/sacog_config_entities.py b/footprint/client/configuration/sacog/config_entity/sacog_config_entities.py new file mode 100644 index 000000000..7e9f3afec --- /dev/null +++ b/footprint/client/configuration/sacog/config_entity/sacog_config_entities.py @@ -0,0 +1,110 @@ +from footprint.client.configuration.fixture import ConfigEntitiesFixture +from footprint.main.models.category import Category +from footprint.main.models.config.scenario import BaseScenario, FutureScenario + +__author__ = 'calthorpe_associates' +from django.contrib.gis.geos import MultiPolygon, Polygon + +__author__ = 'calthorpe_associates' + + +class SacogConfigEntitiesFixture(ConfigEntitiesFixture): + def project_key(self): + return None + + def region_key(self): + return 'sacog' + + def regions(self): + return [ + { + 'key': 'sacog', + 'name': 'SACOG', + 'description': 'The SACOG region', + 'bounds': MultiPolygon([Polygon(( + (-122.719, 37.394), # bottom left + (-122.719, 38.059), # top left + (-121.603, 38.059), # top right + (-121.603, 37.394), # bottom right + (-122.719, 37.394), # bottom leftsample_config_entities + ))]) + }, + ] + + def projects(self, region=None): + return [ + + # { + # 'key': 'sacramento_county', + # 'name': 'Sacramento County', + # 'description': "County of Sacramento", + # 'base_year': 2013, + # 'region_index': 0, + # 'media': [ + # MediumFixture(self.schema, key=ConfigEntityMediumKey.Fab.ricate('irvine_logo'), name='Irvine Logo', + # url='/static/client/{0}/logos/cityofirvine.png'.format(CLIENT)) + # ], + # 'bounds': MultiPolygon([Polygon( + # ( + # # Sacramento County bounds + # (-121.862622000787, 38.018420999589), # bottom left + # (-121.862622000787, 38.7364049988308), # top left + # (-121.027084001338, 38.7364049988308), # top right + # (-121.027084001338, 38.018420999589), # top right + # (-121.862622000787, 38.018420999589) # bottom left + # ) + # )]) + # } + { + 'key': 'sutter_county', + 'name': 'Sutter County', + 'description': "Sutter County", + 'base_year': 2013, + 'region_index': 0, + 'media': [], + 'bounds': MultiPolygon([Polygon( + ( + (-121.738056, 38.553889), + (-121.738056, 38.553889), + (-121.738056, 38.553889), + (-121.738056, 38.553889), + (-121.738056, 38.553889), + ) + )]) + } + + ] + + def scenarios(self, project=None, class_scope=None): + return self.matching_scope([ + { + 'class_scope': BaseScenario, + 'key': '{0}_base'.format(project.key), + 'scope': project.schema(), + 'name': 'Base', + 'description': '{0} Base Scenario {1}'.format('2012', project.name), + 'year': 2012, + 'selections': dict(built_form_sets='sacog_buildingtypes'), + 'categories': [Category(key='category', value='Base')] + }, + { + 'class_scope': FutureScenario, + 'key': '{0}_scenario_a'.format(project.key), + 'scope': project.schema(), + 'name': 'Scenario A', + 'description': 'Future Scenario for {0}'.format(project.name), + 'year': 2050, + 'selections': dict(built_form_sets='sacog_buildingtypes'), + 'categories': [Category(key='category', value='Future')] + }, + { + 'class_scope': FutureScenario, + 'key': '{0}_scenario_b'.format(project.key), + 'scope': project.schema(), + 'name': 'Scenario B', + 'description': 'Future Scenario for {0}'.format(project.name), + 'year': 2050, + 'selections': dict(built_form_sets='sacog_buildingtypes'), + 'categories': [Category(key='category', value='Future')] + }], project_key=project.key if project else None, class_scope=class_scope) + diff --git a/footprint/client/configuration/sacog/config_entity/sacog_project.py b/footprint/client/configuration/sacog/config_entity/sacog_project.py new file mode 100644 index 000000000..1f7997c53 --- /dev/null +++ b/footprint/client/configuration/sacog/config_entity/sacog_project.py @@ -0,0 +1,109 @@ +from footprint.client.configuration.sacog.base.sacog_light_rail_feature import SacogLightRailFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_feature import SacogLightRailStopsFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_quarter_mile_feature \ + import SacogLightRailStopsQuarterMileFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_one_mile_feature \ + import SacogLightRailStopsOneMileFeature +from footprint.client.configuration.sacog.base.sacog_light_rail_stops_half_mile_feature \ + import SacogLightRailStopsHalfMileFeature +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.client.configuration.fixture import ProjectFixture +from footprint.client.configuration.sacog.base.sacog_existing_land_use_parcel_feature import SacogExistingLandUseParcelFeature +from footprint.client.configuration.sacog.base.sacog_hardwood_feature import SacogHardwoodFeature +from footprint.client.configuration.sacog.base.sacog_stream_feature import SacogStreamFeature +from footprint.client.configuration.sacog.base.sacog_vernal_pool_feature import SacogVernalPoolFeature +from footprint.client.configuration.sacog.base.sacog_wetland_feature import SacogWetlandFeature +from footprint.main.lib.functions import merge +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +class SacogProjectFixture(ProjectFixture): + def feature_class_lookup(self): + """ + Adds mappings of custom Feature classes + :return: + """ + parent_fixture = self.parent_fixture + feature_class_lookup = parent_fixture.feature_class_lookup() + return merge( + feature_class_lookup, + FeatureClassCreator.db_entity_key_to_feature_class_lookup(self.config_entity, self.default_db_entity_configurations()) + ) + + def default_db_entity_configurations(self): + """ + Project specific SACOG additional db_entities + :param default_dict: + :return: + """ + + config_entity = self.config_entity + parent_fixture = self.parent_fixture + defaults = parent_fixture.default_db_entity_configurations() + + return super(SacogProjectFixture, self).default_db_entity_configurations() + [ + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_SACOG_EXISTING_LAND_USE_PARCEL_SOURCE, + base_class=SacogExistingLandUseParcelFeature, + intersection=dict(type='attribute', db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE), + primary_key='geography_id', + primary_key_type='varchar', + fields=dict(), + related_fields=dict(land_use_definition=dict( + single=True, + related_class_name='footprint.client.configuration.sacog.built_form.sacog_land_use_definition.SacogLandUseDefinition', + # Use this for the resource type, since we don't want a client-specific resource URL + # TODO not wired up yet + resource_model_class_name='footprint.main.models.built_form.ClientLandUseDefinition', + related_class_join_field_name='land_use', + source_class_join_field_name='land_use')), + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_VERNAL_POOL_FEATURE, + base_class=SacogVernalPoolFeature, + intersection=dict(type='polygon') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_WETLAND_FEATURE, + base_class=SacogWetlandFeature, + intersection=dict(type='polygon') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_STREAM_FEATURE, + base_class=SacogStreamFeature, + intersection=dict(type='polygon') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_HARDWOOD_FEATURE, + base_class=SacogHardwoodFeature, + intersection=dict(type='polygon') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_LIGHT_RAIL_FEATURE, + base_class=SacogLightRailFeature, + intersection=dict(type='polygon') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_LIGHT_RAIL_STOPS_FEATURE, + base_class=SacogLightRailStopsFeature, + intersection=dict(type='polygon') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_LIGHT_RAIL_STOPS_ONE_MILE_FEATURE, + base_class=SacogLightRailStopsOneMileFeature, + intersection=dict(type='polygon', to='centroid') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_LIGHT_RAIL_STOPS_HALF_MILE_FEATURE, + base_class=SacogLightRailStopsHalfMileFeature, + intersection=dict(type='polygon', to='centroid') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_LIGHT_RAIL_STOPS_QUARTER_MILE_FEATURE, + base_class=SacogLightRailStopsQuarterMileFeature, + intersection=dict(type='polygon', to='centroid') + ) + ] \ No newline at end of file diff --git a/footprint/client/configuration/sacog/config_entity/sacog_region.py b/footprint/client/configuration/sacog/config_entity/sacog_region.py new file mode 100644 index 000000000..111797649 --- /dev/null +++ b/footprint/client/configuration/sacog/config_entity/sacog_region.py @@ -0,0 +1,38 @@ + +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration +from footprint.client.configuration.fixture import RegionFixture +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +class SacogRegionFixture(RegionFixture): + + def default_remote_db_entity_configurations(self): + """ + Add the SACOG background. This function is called from default_db_entity_configurations so it doesn't + need to call the parent_fixture's method + """ + return [dict( + key='sacog_background', + url="http://services.sacog.org/arcgis/rest/services/Imagery_DigitalGlobe_2012WGS/MapServer/tile/{Z}/{Y}/{X}", + no_feature_class_configuration=True + )] + + def default_db_entity_configurations(self): + """ + Region specific SACOG db_entity_setups + :param default_dict: + :return: + """ + + config_entity = self.config_entity + parent_region_fixture = self.parent_fixture + default_db_entity_configurations = parent_region_fixture.default_db_entity_configurations() + + remote_db_entity_setups = map( + lambda remote_setup: create_db_entity_configuration(config_entity, **remote_setup), + self.default_remote_db_entity_configurations()) + + return default_db_entity_configurations + remote_db_entity_setups + diff --git a/footprint/client/configuration/sacog/policy/__init__.py b/footprint/client/configuration/sacog/policy/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/sacog/policy/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/sacog/publishing/__init__.py b/footprint/client/configuration/sacog/publishing/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/sacog/publishing/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/sacog/publishing/sacog_land_use_symbology.py b/footprint/client/configuration/sacog/publishing/sacog_land_use_symbology.py new file mode 100644 index 000000000..3d173d59c --- /dev/null +++ b/footprint/client/configuration/sacog/publishing/sacog_land_use_symbology.py @@ -0,0 +1,80 @@ +from footprint.client.configuration.fixture import LandUseSymbologyFixture + +__author__ = 'calthorpe_associates' + + +class SacogLandUseSymbology(LandUseSymbologyFixture): + """ + a resource for translating SACOG's land uses into UF Built Forms + """ + + def land_use_color_lookup(self): + return { + "Very High Density Attached Residential": '#993300', + "Medium-High Density Attached Residential": '#F27900', + "Medium Density Attached Residential": '#FF9900', + "High Density Attached Residential": '#CC6600', + "Urban Attached Residential": '#7A2900', + + + "Very Low Density Detached Residential": "#FFFFCC", + "Low Density Detached Residential": "#FFFF00", + "Medium Density Detached Residential": '#FABF8F', + "Medium-High Density Detached Residential": '#FFCC66', + + "LARGE LOT NOT FARM HOME": "#FFFFCC", + "Rural Residential": "#FFFFCC", + "Farm Home": "#99FF99", + + "Mobile Home Park": "#FF6600", + + "Moderate-Intensity Office": '#FF99CC', + "High-Intensity Office": '#FF66CC', + "CBD Office": '#FF3399', + + "Light Industrial-Office": '#CC99FF', + + "Medical Facility": '#404040', + + "Agriculture": "#948A54", + "Agricultural Employment": "#948A54", + + "Residential/Retail Mixed Use Low": '#66CCFF', + "Residential/Retail Mixed Use High": '#3399FF', + "High-Rise Mixed Use": "#66FFFF", + "Mid-Rise Mixed Use": "#00FFFF", + "Urban Mid-Rise Residential": "#009999", + + "Hotel": '#963634', + "Regional Retail": "#CC0000", + "Regional Commercial/Office": '#800000', + + "Heavy Industrial": '#9900CC', + "Light Industrial": '#9933FF', + + "K-12 School": '#808080', + "Colleges and Universities": '#595959', + "UC DAVIS": '#595959', + + "Civic/Institution": '#BFBFBF', + "Public/Quasi-Public": '#D9D9D9', + + "Community/Neighborhood Retail": '#FB3300', + "Community/Neighborhood Commercial": '#FF0000', + "Community/Neighborhood Commercial/Office": '#FF0000', + + "Parking Lot": '#C4BD97', + "Parking Structure": '#948A54', + "Parking Structure w/Ground Floor Retail": "#494529", + + "Agricultural Processing/Retail Employment": "#948A54", + "Park and/or Open Space": '#92D050', + "Airport": "#262626", + + "Military": "#CCCCFF", + + "Blank Place Type": None, + "Water": None, + "Roads": None, + "Urban Reserve": None, + } \ No newline at end of file diff --git a/footprint/client/configuration/sacog/publishing/sacog_layer.py b/footprint/client/configuration/sacog/publishing/sacog_layer.py new file mode 100644 index 000000000..eda2b9a7e --- /dev/null +++ b/footprint/client/configuration/sacog/publishing/sacog_layer.py @@ -0,0 +1,121 @@ +from footprint.client.configuration.fixture import LayerConfigurationFixture +from footprint.client.configuration.mixins.publishing.layer_primary_base import primary_base_template_context_dict +from footprint.main.models.config.scenario import BaseScenario, FutureScenario, Scenario +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.presentation_configuration import LayerConfiguration +from footprint.client.configuration.sacog.built_form.sacog_land_use_definition import SacogLandUseDefinition + + + +__author__ = 'calthorpe_associates' + + +class SacogLayerConfigurationFixture(LayerConfigurationFixture): + def layer_libraries(self, layers=None): + return self.parent_fixture.layer_libraries( + self.matching_scope(self.layers(), class_scope=self.config_entity and self.config_entity.__class__)) + + def layers(self): + return self.parent_fixture.layers() + [ + # Only used by BaseScenarios + LayerConfiguration( + scope=BaseScenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_SACOG_EXISTING_LAND_USE_PARCEL_SOURCE, + visible=True, + visible_attributes=['land_use_definition__id'], + column_alias_lookup=dict(land_use_definition__id='land_use_definition_id'), + built_form_set_key='sacog_land_use', + template_context_dict=primary_base_template_context_dict(SacogLandUseDefinition) + ), + LayerConfiguration( + # Show in both base and future Scenarios! + scope=FutureScenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_SACOG_EXISTING_LAND_USE_PARCEL_SOURCE, + visible=False, + visible_attributes=['land_use_definition__id'], + column_alias_lookup=dict(land_use_definition__id='land_use_definition_id'), + built_form_set_key='sacog_land_use', + template_context_dict=primary_base_template_context_dict(SacogLandUseDefinition) + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_STREAM_FEATURE, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_VERNAL_POOL_FEATURE, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_WETLAND_FEATURE, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_HARDWOOD_FEATURE, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_LIGHT_RAIL_FEATURE, + visible=False, + visible_attributes=['line'], + template_context_dict={'attributes': {'line': {'unstyled': True}}} + ), + + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_LIGHT_RAIL_STOPS_FEATURE, + visible=False, + visible_attributes=['color'], + template_context_dict={'attributes': {'color': {'unstyled': True}}} + ), + + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_LIGHT_RAIL_STOPS_ONE_MILE_FEATURE, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_LIGHT_RAIL_STOPS_HALF_MILE_FEATURE, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_LIGHT_RAIL_STOPS_QUARTER_MILE_FEATURE, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ) + + ] + + def update_or_create_templates(self): + """ + Delegates to default, which will also create templates for the client's custom layers + :return: + """ + self.parent_fixture.update_or_create_templates() + + def import_layer_configurations(self): + return self.parent_fixture.import_layer_configurations() \ No newline at end of file diff --git a/footprint/client/configuration/sacog/publishing/sacog_result.py b/footprint/client/configuration/sacog/publishing/sacog_result.py new file mode 100644 index 000000000..b11ad7638 --- /dev/null +++ b/footprint/client/configuration/sacog/publishing/sacog_result.py @@ -0,0 +1,57 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.client.configuration.fixture import ResultConfigurationFixture +from footprint.main.publishing.result_initialization import ResultConfiguration, ResultLibraryKey, ResultKey, ResultSort +from footprint.main.models.config.scenario import BaseScenario +from footprint.main.models.keys.keys import Keys + + +class SacogResultConfigurationFixtures(ResultConfigurationFixture): + def results(self): + """ + Used to update or create Results per ConfigEntity instance + Returns the result library(ies) scoped for the class of self.config_entity. + The Result will belong to the ResultLibrary specified by result_library_key + :return: + """ + return self.matching_scope( + # Basic Core result query that summarizes increments + self.parent_fixture.results() + [ + + # Aggregate result from the Analytic Bars + ResultConfiguration( + class_scope=BaseScenario, + result_type='analytic_bars', + result_library_key=ResultLibraryKey.DEFAULT, + result_db_entity_key=ResultKey.Fab.ricate('base_bars'), + source_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + + name='Base Results', + attributes=['employment', 'dwelling_units', 'population'], + db_column_lookup=dict( + employment='emp', + dwelling_units='du', + population='pop' + ), + extent_lookup=dict( + population=dict(min=0, max=5000000), + dwelling_units=dict(min=0, max=1000000), + employment=dict(min=0, max=1000000) + ), + labels=['Employees', 'Dwelling Units', 'Population'], + stackable=False, + create_query=self.simple_aggregate, + sort_priority=ResultSort.BASE + ), + ], + class_scope=self.config_entity and self.config_entity.__class__) diff --git a/footprint/client/configuration/sacog/resource/__init__.py b/footprint/client/configuration/sacog/resource/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/sacog/resource/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/sacog/resource/sacog_resource.py b/footprint/client/configuration/sacog/resource/sacog_resource.py new file mode 100644 index 000000000..7a9375c87 --- /dev/null +++ b/footprint/client/configuration/sacog/resource/sacog_resource.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com diff --git a/footprint/client/configuration/sacog/sacog_init.py b/footprint/client/configuration/sacog/sacog_init.py new file mode 100644 index 000000000..b746cd04b --- /dev/null +++ b/footprint/client/configuration/sacog/sacog_init.py @@ -0,0 +1,61 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +import os +from django.core.management import call_command +from django.conf import settings + +from footprint.client.configuration.fixture import InitFixture +from footprint.client.configuration.sacog.built_form.sacog_land_use_definition import SacogLandUseDefinition + +class SacogInitFixture(InitFixture): + def model_class_modules(self): + """ + SACOG defines additional concrete model classes in the following modules + :return: + """ + return [ + ("built_form", "land_use_definition"), + ("built_form", "land_use") + ] + + def import_database(self): + if settings.USE_SAMPLE_DATA_SETS and settings.USE_LOCAL_SAMPLE_DATA_SETS: + dct = settings.DATABASES['sample_data'] + return dict( + host=dct['HOST'], + database=dct['NAME'], + user=dct['USER'], + password=dct['PASSWORD'] + ) + else: + return dict( + host='10.0.0.133', + database='sacog_urbanfootprint', + user='calthorpe', + password='[PASSWORD]' + ) + + def populate_models(self): + if SacogLandUseDefinition.objects.count() == 0: + print "loading sacog land uses" + fixture_path = os.path.join(settings.SERVER_ROOT, 'footprint', 'client', 'configuration', + 'sacog', 'built_form', 'sacog_land_use_definitions.json') + call_command('loaddata', fixture_path) + else: + print "skipping because of " + str(SacogLandUseDefinition.objects.count()) + " objects already there" + + def users(self): + return [ + dict(username='test', password='test', email='testy@test.ca', api_key='TEST_API_KEY'), + dict(username='raef', password='raef', email='raef@sacog.gov'), + dict(username='jennifer', password='test', email='jennifer@test.ca', api_key='TEST_API_KEY'), + ] diff --git a/footprint/client/configuration/sandag/__init__.py b/footprint/client/configuration/sandag/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/sandag/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/sandag/base/__init__.py b/footprint/client/configuration/sandag/base/__init__.py new file mode 100644 index 000000000..221c91da7 --- /dev/null +++ b/footprint/client/configuration/sandag/base/__init__.py @@ -0,0 +1,21 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +__author__ = 'calthorpe_associates' + diff --git a/footprint/client/configuration/sandag/base/sandag_2050_rtp_transit_network_feature.py b/footprint/client/configuration/sandag/base/sandag_2050_rtp_transit_network_feature.py new file mode 100644 index 000000000..b0ae10c19 --- /dev/null +++ b/footprint/client/configuration/sandag/base/sandag_2050_rtp_transit_network_feature.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class Sandag2050RtpTransitNetworkFeature(Feature): + + transit_mode = models.IntegerField(null=False) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/client/configuration/sandag/base/sandag_2050_rtp_transit_stop_feature.py b/footprint/client/configuration/sandag/base/sandag_2050_rtp_transit_stop_feature.py new file mode 100644 index 000000000..78b78ae47 --- /dev/null +++ b/footprint/client/configuration/sandag/base/sandag_2050_rtp_transit_stop_feature.py @@ -0,0 +1,41 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class Sandag2050RtpTransitStopFeature(Feature): + + transit_mode = models.IntegerField(null=False) + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateSandag2050RtpTransitStopFeature(Sandag2050RtpTransitStopFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/sandag/base/sandag_scenario_a_boundary.py b/footprint/client/configuration/sandag/base/sandag_scenario_a_boundary.py new file mode 100644 index 000000000..e3300f3c5 --- /dev/null +++ b/footprint/client/configuration/sandag/base/sandag_scenario_a_boundary.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class SandagScenarioABoundaryFeature(Feature): + + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateSandagScenarioABoundaryFeature(SandagScenarioABoundaryFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/sandag/base/sandag_scenario_b_boundary.py b/footprint/client/configuration/sandag/base/sandag_scenario_b_boundary.py new file mode 100644 index 000000000..d14f0b67d --- /dev/null +++ b/footprint/client/configuration/sandag/base/sandag_scenario_b_boundary.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class SandagScenarioBBoundaryFeature(Feature): + + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateSandagScenarioBBoundaryFeature(SandagScenarioBBoundaryFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/sandag/base/sandag_scenario_c_boundary.py b/footprint/client/configuration/sandag/base/sandag_scenario_c_boundary.py new file mode 100644 index 000000000..9a1151480 --- /dev/null +++ b/footprint/client/configuration/sandag/base/sandag_scenario_c_boundary.py @@ -0,0 +1,40 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class SandagScenarioCBoundaryFeature(Feature): + + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateSandagScenarioCBoundaryFeature(SandagScenarioCBoundaryFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/sandag/built_form/__init__.py b/footprint/client/configuration/sandag/built_form/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/sandag/built_form/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/sandag/config_entity/__init__.py b/footprint/client/configuration/sandag/config_entity/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/sandag/config_entity/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/sandag/config_entity/sandag_config_entities.py b/footprint/client/configuration/sandag/config_entity/sandag_config_entities.py new file mode 100644 index 000000000..8747475a5 --- /dev/null +++ b/footprint/client/configuration/sandag/config_entity/sandag_config_entities.py @@ -0,0 +1,111 @@ +from footprint.client.configuration.fixture import ConfigEntitiesFixture, MediumFixture +from footprint.client.configuration.default.config_entity.default_config_entities import ConfigEntityMediumKey +from footprint.main.models.config.scenario import BaseScenario, FutureScenario +from django.conf import settings + +__author__ = 'calthorpe_associates' +from django.contrib.gis.geos import MultiPolygon, Polygon +from footprint.main.mixins.category import Category + + + +class SandagConfigEntitiesFixture(ConfigEntitiesFixture): + def regions(self): + return [ + { + 'key': 'sandag', + 'name': 'SANDAG', + 'description': 'The San Diego region', + 'bounds': MultiPolygon([Polygon(( + (-122.719, 37.394), # bottom left + (-122.719, 38.059), # top left + (-121.603, 38.059), # top right + (-121.603, 37.394), # bottom right + (-122.719, 37.394), # bottom leftsample_config_entities + ))]) + }, + ] + + def projects(self, region=None): + return [ + + { + 'key': 'sandag', + 'name': 'SANDAG Scenarios', + 'description': "The San Diego Region", + 'base_year': 2012, + 'region_index': 0, + 'media': [ + MediumFixture(key=ConfigEntityMediumKey.Fab.ricate('sandag_logo'), name='Sandag Logo', + url='/static/client/{0}/logos/sandag.png'.format(settings.CLIENT)) + ], + 'bounds': MultiPolygon([Polygon( + ( + # TODO: decide if this needs updating or if it's taken care of by the project method + # Sacramento County bounds + (-121.862622000787, 38.018420999589), # bottom left + (-121.862622000787, 38.7364049988308), # top left + (-121.027084001338, 38.7364049988308), # top right + (-121.027084001338, 38.018420999589), # top right + (-121.862622000787, 38.018420999589) # bottom left + ) + )]) + } + ] + + def scenarios(self, project=None, class_scope=None): + return self.matching_scope([ + { + 'class_scope': BaseScenario, + 'key': '{0}_base'.format(project.key), + 'scope': project.schema(), + 'name': 'Base', + 'description': '{0} Base Scenario {1}'.format('2012', project.name), + 'year': 2012, + 'selections': dict(), + 'categories': [Category(key='category', value='Base')] + }, + + { + 'class_scope': FutureScenario, + 'key': '{0}_series13'.format(project.key), + 'scope': project.schema(), + 'name': 'Series 13', + 'description': '{0} Future Scenario Series13 for {1}'.format('2050', project.name), + 'year': 2050, + 'selections': dict(), + 'categories': [Category(key='category', value='Future')] + }, + + { + 'class_scope': FutureScenario, + 'key': '{0}_scenario_b'.format(project.key), + 'scope': project.schema(), + 'name': 'Scenario B', + 'description': '{0} Future Scenario Alternative B for {1}'.format('2050', project.name), + 'year': 2050, + 'selections': dict(), + 'categories': [Category(key='category', value='Future')] + }, + { + 'class_scope': FutureScenario, + 'key': '{0}_scenario_c'.format(project.key), + 'scope': project.schema(), + 'name': 'Scenario C'.format(project.name), + 'description': '{0} Future Scenario Alternative C for {1}'.format('2050', project.name), + 'year': 2050, + 'selections': dict(), + 'categories': [Category(key='category', value='Future')] + }, + { + 'class_scope': FutureScenario, + 'key': '{0}_series9'.format(project.key), + 'scope': project.schema(), + 'name': 'Series 9', + 'description': '{0} Future Scenario Series9 for {1}'.format('2050', project.name), + 'year': 2050, + 'selections': dict(), + 'categories': [Category(key='category', value='Future')] + }, + ], project_key=project.key if project else None, class_scope=class_scope) + diff --git a/footprint/client/configuration/sandag/config_entity/sandag_project.py b/footprint/client/configuration/sandag/config_entity/sandag_project.py new file mode 100644 index 000000000..6f26729c2 --- /dev/null +++ b/footprint/client/configuration/sandag/config_entity/sandag_project.py @@ -0,0 +1,60 @@ +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.client.configuration.fixture import ProjectFixture +from footprint.client.configuration.sandag.base.sandag_2050_rtp_transit_network_feature import Sandag2050RtpTransitNetworkFeature +from footprint.client.configuration.sandag.base.sandag_2050_rtp_transit_stop_feature import Sandag2050RtpTransitStopFeature +from footprint.client.configuration.sandag.base.sandag_scenario_b_boundary import SandagScenarioBBoundaryFeature +from footprint.client.configuration.sandag.base.sandag_scenario_c_boundary import SandagScenarioCBoundaryFeature +from footprint.client.configuration.utils import resolve_default_fixture +from footprint.main.lib.functions import merge +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +class SandagProjectFixture(ProjectFixture): + def feature_class_lookup(self): + """ + Adds mappings of custom Feature classes + :return: + """ + default_project = resolve_default_fixture("config_entity", "project", ProjectFixture, config_entity=None) + feature_class_lookup = default_project.feature_class_lookup() + return merge( + feature_class_lookup, + FeatureClassCreator.db_entity_key_to_feature_class_lookup(self.config_entity, self.default_db_entity_configurations()) + ) + + def default_db_entity_configurations(self): + """ + Project specific SACOG additional db_entities + :param default_dict: + :return: + """ + + default_project_fixture = self.parent_fixture + defaults = default_project_fixture.default_db_entity_configurations() + config_entity = self.config_entity + + return super(SandagProjectFixture, self).default_db_entity_configurations() + [ + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_SANDAG_SCENARIO_B_BOUNDARY, + base_class=SandagScenarioBBoundaryFeature, + intersection=dict(type='polygon', to='centroid'), + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_SANDAG_SCENARIO_C_BOUNDARY, + base_class=SandagScenarioCBoundaryFeature, + intersection=dict(type='polygon', to='centroid'), + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_SANDAG_2050_RTP_TRANSIT_NETWORK, + base_class=Sandag2050RtpTransitNetworkFeature, + intersection=dict(type='polygon') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_SANDAG_2050_RTP_TRANSIT_STOPS, + base_class=Sandag2050RtpTransitStopFeature, + intersection=dict(type='polygon') + ), + ] diff --git a/footprint/client/configuration/sandag/policy/__init__.py b/footprint/client/configuration/sandag/policy/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/sandag/policy/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/sandag/publishing/__init__.py b/footprint/client/configuration/sandag/publishing/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/sandag/publishing/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/sandag/publishing/sandag_layer.py b/footprint/client/configuration/sandag/publishing/sandag_layer.py new file mode 100644 index 000000000..fe02489e8 --- /dev/null +++ b/footprint/client/configuration/sandag/publishing/sandag_layer.py @@ -0,0 +1,61 @@ +from footprint.client.configuration.fixture import LayerConfigurationFixture +from footprint.main.models import Scenario +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.presentation_configuration import LayerConfiguration + +__author__ = 'calthorpe_associates' + + +class SandagLayerConfigurationFixture(LayerConfigurationFixture): + def layer_libraries(self, layers=None): + return self.parent_fixture.layer_libraries( + self.matching_scope(self.layers(), class_scope=self.config_entity and self.config_entity.__class__)) + + def layers(self): + return self.parent_fixture.layers() + [ + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_SANDAG_SCENARIO_A_BOUNDARY, + visible=False, + visible_attributes=['geography_id'], + template_context_dict={'attributes': {'geography_id': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_SANDAG_SCENARIO_B_BOUNDARY, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_SANDAG_SCENARIO_C_BOUNDARY, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_SANDAG_2050_RTP_TRANSIT_NETWORK, + visible=False, + visible_attributes=['transit_mode'], + template_context_dict={'attributes': {'transit_mode': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_SANDAG_2050_RTP_TRANSIT_STOPS, + visible=False, + visible_attributes=['transit_mode'], + template_context_dict={'attributes': {'transit_mode': {'unstyled': True}}} + ), + ] + + def update_or_create_templates(self): + """ + Delegates to default, which will also create templates for the client's custom layers + :return: + """ + self.parent_fixture.update_or_create_templates() + + def import_layer_configurations(self): + return self.parent_fixture.import_layer_configurations() diff --git a/footprint/client/configuration/sandag/publishing/sandag_result.py b/footprint/client/configuration/sandag/publishing/sandag_result.py new file mode 100644 index 000000000..11dad0f79 --- /dev/null +++ b/footprint/client/configuration/sandag/publishing/sandag_result.py @@ -0,0 +1,57 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.client.configuration.fixture import ResultConfigurationFixture +from footprint.main.publishing.result_initialization import ResultConfiguration, ResultLibraryKey, ResultKey, ResultSort +from footprint.main.models.config.scenario import BaseScenario +from footprint.main.models.keys.keys import Keys + + +class SandagResultConfigurationFixtures(ResultConfigurationFixture): + def results(self): + """ + Used to update or create Results per ConfigEntity instance + Returns the result library(ies) scoped for the class of self.config_entity. + The Result will belong to the ResultLibrary specified by result_library_key + :return: + """ + return self.matching_scope( + # Basic Core result query that summarizes increments + super(SandagResultConfigurationFixtures, self).results() + [ + + # Aggregate result from the Analytic Bars + ResultConfiguration( + class_scope=BaseScenario, + result_type='analytic_bars', + result_library_key=ResultLibraryKey.DEFAULT, + result_db_entity_key=ResultKey.Fab.ricate('base_bars'), + source_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + + name='Base Results', + attributes=['employment', 'dwelling_units', 'population'], + db_column_lookup=dict( + employment='emp', + dwelling_units='du', + population='pop' + ), + extent_lookup=dict( + population=dict(min=0, max=5000000), + dwelling_units=dict(min=0, max=1000000), + employment=dict(min=0, max=1000000) + ), + labels=['Employees', 'Dwelling Units', 'Population'], + stackable=False, + create_query=self.simple_aggregate, + sort_priority=ResultSort.BASE + ), + ], + class_scope=self.config_entity and self.config_entity.__class__) diff --git a/footprint/client/configuration/sandag/sandag_init.py b/footprint/client/configuration/sandag/sandag_init.py new file mode 100644 index 000000000..c930d205f --- /dev/null +++ b/footprint/client/configuration/sandag/sandag_init.py @@ -0,0 +1,11 @@ +__author__ = 'calthorpe_associates' + +from footprint.client.configuration.fixture import InitFixture + +class SandagInitFixture(InitFixture): + def import_database(self): + return dict( + host='10.0.0.133', + database='urbanfootprint_dev', + user='footprint', + password='[PASSWORD]') diff --git a/footprint/client/configuration/scag/__init__.py b/footprint/client/configuration/scag/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/scag/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/scag/base/__init__.py b/footprint/client/configuration/scag/base/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/scag/base/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/scag/base/scag_transit_areas_feature.py b/footprint/client/configuration/scag/base/scag_transit_areas_feature.py new file mode 100644 index 000000000..852904503 --- /dev/null +++ b/footprint/client/configuration/scag/base/scag_transit_areas_feature.py @@ -0,0 +1,9 @@ +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class ScagTransitAreasFeature(Feature): + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/client/configuration/scag/built_form/__init__.py b/footprint/client/configuration/scag/built_form/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/scag/built_form/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/scag/built_form/scag_2012_landuse_codes.sql b/footprint/client/configuration/scag/built_form/scag_2012_landuse_codes.sql new file mode 100644 index 000000000..8f056cff8 --- /dev/null +++ b/footprint/client/configuration/scag/built_form/scag_2012_landuse_codes.sql @@ -0,0 +1,173 @@ +-- +-- PostgreSQL database dump +-- + +SET statement_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SET check_function_bodies = false; +SET client_min_messages = warning; + +SET search_path = irvine_datasets, pg_catalog; + +SET default_tablespace = ''; + +SET default_with_oids = false; + +-- +-- Name: scag_2012_landuse_codes; Type: TABLE; Schema: irvine_datasets; Owner: calthorpe; Tablespace: +-- + +CREATE TABLE scag_2012_landuse_codes ( + landuse_id integer, + landuse_description character varying, + landuse_type character varying +); + + +ALTER TABLE irvine_datasets.scag_2012_landuse_codes OWNER TO calthorpe; + +-- +-- Data for Name: scag_2012_landuse_codes; Type: TABLE DATA; Schema: irvine_datasets; Owner: calthorpe +-- + +COPY scag_2012_landuse_codes (landuse_id, landuse_description, landuse_type) FROM stdin; +1000 Urban or Built-Up Urban or Built-Up +1100 Residential Mixed Residential +1110 Single Family Residential Single Family Residential +1111 High-Density Single Family Residential Single Family Residential +1112 Low-Density Single Family Residential Single Family Residential +1113 Rural Residential Single Family Residential +1120 Multi-Family Residential (Medium-High Density Residential) Multi-Family Residential +1121 Mixed Multi-Family Residential Multi-Family Residential +1122 Duplexes, Triplexes and 2- or 3-Unit Condominiums and Townhouses Multi-Family Residential +1123 Low-Rise Apartments, Condominiums, and Townhouses Multi-Family Residential +1124 Medium-Rise Apartments and Condominiums Multi-Family Residential +1125 High-Rise Apartments and Condominiums Multi-Family Residential +1130 Mobile Homes and Trailer Parks Mobile Homes and Trailer Parks +1131 Trailer Parks and Mobile Home Courts, High-Density Mobile Homes and Trailer Parks +1132 Mobile Home Courts and Subdivisions, Low-Density Mobile Homes and Trailer Parks +1140 Mixed Residential (single and multiple residential) Mixed Residential +1200 Commercial and Services Commercial and Services +1210 General Office Use General Office +1211 Low- and Medium-Rise Major Office Use General Office +1212 High-Rise Major Office Use General Office +1213 Skyscrapers General Office +1220 Retail Stores and Commercial Services Commercial and Services +1221 Regional Shopping Center Commercial and Services +1222 Retail Centers (Non-Strip With Contiguous Interconnected Off-Street Parking) Commercial and Services +1223 Retail Strip Development Commercial and Services +1230 Other Commercial Commercial and Services +1231 Commercial Storage Commercial and Services +1232 Commercial Recreation Commercial and Services +1233 Hotels and Motels Commercial and Services +1240 Public Facilities Facilities +1241 Government Offices Facilities +1242 Police and Sheriff Stations Facilities +1243 Fire Stations Facilities +1244 Major Medical Health Care Facilities Facilities +1245 Religious Facilities Facilities +1246 Other Public Facilities Facilities +1247 Public Parking Facilities Facilities +1250 Special Use Facilities Facilities +1251 Correctional Facilities Facilities +1252 Special Care Facilities Facilities +1253 Other Special Use Facilities Facilities +1260 Educational Institutions Education +1261 Pre-Schools/Day Care Centers Education +1262 Elementary Schools Education +1263 Junior or Intermediate High Schools Education +1264 Senior High Schools Education +1265 Colleges and Universities Education +1266 Trade Schools and Professional Training Facilities Education +1270 Military Installations Military Installations +1271 Base (Built-up Area) Military Installations +1272 Vacant Area Military Installations +1273 Air Field Military Installations +1274 Former Base (Built-up Area) Military Installations +1275 Former Base Vacant Area Military Installations +1276 Former Base Air Field Military Installations +1300 Industrial Industrial +1310 Light Industrial Industrial +1311 Manufacturing, Assembly, and Industrial Services Industrial +1312 Picture and Television Production Lots Industrial +1313 Packing Houses and Grain Elevators Industrial +1314 Research and Development Industrial +1320 Heavy Industrial Industrial +1321 Manufacturing Industrial +1322 Petroleum Refining and Processing Industrial +1323 Open Storage Industrial +1324 Major Metal Processing Industrial +1325 Chemical Processing Industrial +1330 Extraction Industrial +1331 Mineral Extraction - Other Than Oil and Gas Industrial +1332 Mineral Extraction - Oil and Gas Industrial +1340 Wholesaling and Warehousing Industrial +1400 Transportation, Communications, and Utilities Transportation, Communications, and Utilities +1410 Transportation Transportation, Communications, and Utilities +1411 Airports Transportation, Communications, and Utilities +1412 Railroads Transportation, Communications, and Utilities +1413 Freeways and Major Roads Transportation, Communications, and Utilities +1414 Carpool and Rideshare Facilities Transportation, Communications, and Utilities +1415 Bus Terminals and Transit Centers Transportation, Communications, and Utilities +1416 Truck Terminals Transportation, Communications, and Utilities +1417 Harbor Facilities Transportation, Communications, and Utilities +1418 Navigation Aids Transportation, Communications, and Utilities +1420 Communication Facilities Transportation, Communications, and Utilities +1430 Utility Facilities Transportation, Communications, and Utilities +1431 Electrical Power Facilities Transportation, Communications, and Utilities +1432 Solid Waste Disposal Facilities Transportation, Communications, and Utilities +1433 Liquid Waste Disposal Facilities Transportation, Communications, and Utilities +1434 Water Storage Facilities Transportation, Communications, and Utilities +1435 Natural Gas and Petroleum Facilities Transportation, Communications, and Utilities +1436 Water Transfer Facilities Transportation, Communications, and Utilities +1437 Improved Flood Waterways and Structures Transportation, Communications, and Utilities +1438 Mixed Utilities Transportation, Communications, and Utilities +1440 Maintenance Yards Transportation, Communications, and Utilities +1441 Bus Yards Transportation, Communications, and Utilities +1442 Rail Yards Transportation, Communications, and Utilities +1450 Mixed Transportation Transportation, Communications, and Utilities +1460 Mixed Transportation and Utility Transportation, Communications, and Utilities +1500 Mixed Commercial and Industrial Mixed Commercial and Industrial +1600 Mixed Residential and Commercial Services Mixed Residential and Commercial +1700 Under Construction Under Construction +1800 Open Space and Recreation Open Space and Recreation +1810 Golf Courses Open Space and Recreation +1820 Local Parks and Recreation Open Space and Recreation +1830 State and National Parks and Recreation Open Space and Recreation +1840 Cemeteries Open Space and Recreation +1850 Wildlife Preserves and Sanctuaries Open Space and Recreation +1860 Specimen Gardens and Arboreta Open Space and Recreation +1870 Beach Parks Open Space and Recreation +1880 Other Open Space and Recreation Open Space and Recreation +1900 Urban Vacant (developable) Vacant +2000 Agriculture Agriculture +2100 Cropland and Improved Pasture Land Agriculture +2110 Irrigated Cropland and Improved Pasture Land Agriculture +2120 Non-Irrigated Cropland and Improved Pasture Land Agriculture +2200 Orchards and Vineyards Agriculture +2300 Nurseries Agriculture +2400 Dairy, Intensive Livestock, and Associated Facilities Agriculture +2500 Poultry Operations Agriculture +2600 Other Agriculture Agriculture +2700 Horse Ranches Agriculture +3000 Vacant (developable) Vacant +3100 Vacant Undifferentiated Vacant +3200 Abandoned Orchards and Vineyards Vacant +3300 Vacant With Limited Improvements Vacant +3400 Beaches (vacant) Vacant +4000 Water Water +4100 Water, Undifferentiated Water +4200 Harbor Water Facilities Water +4300 Marina Water Facilities Water +4400 Water Within a Military Installation Water +4500 Area of Inundation (High Water) (1990 Database only) Water +8888 Undevelopable or protected Land Undevelopable or Protected Land +9999 No Photo Coverage/Not in Update Study Area Unknown +\. + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/footprint/client/configuration/scag/built_form/scag_built_form.py b/footprint/client/configuration/scag/built_form/scag_built_form.py new file mode 100644 index 000000000..00618661b --- /dev/null +++ b/footprint/client/configuration/scag/built_form/scag_built_form.py @@ -0,0 +1,74 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.core.management import call_command +from django.template.defaultfilters import slugify + +from footprint.client.configuration.fixture import BuiltFormFixture, LandUseSymbologyFixture +from footprint.client.configuration.scag.built_form.scag_land_use import ScagLandUse +from footprint.client.configuration.scag.built_form.scag_land_use_definition import ScagLandUseDefinition +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.lib.functions import merge +from footprint.main.models.built_form.built_form import update_or_create_built_form_medium +from footprint.main.models.tag import Tag +from footprint import settings + + +class ScagBuiltFormFixture(BuiltFormFixture): + def built_forms(self): + def construct_scag_land_uses(): + if ScagLandUseDefinition.objects.count() == 0: + # TODO throw if this errs + call_command('loaddata', + 'main/initialization/fixtures/client/scag/built_form/scag_land_use_definitions.json') + + land_use_symbology_fixture = resolve_fixture( + "publishing", + "land_use_symbology", + LandUseSymbologyFixture, + settings.CLIENT) + land_use_lookup = land_use_symbology_fixture.land_use_color_lookup() + return map( + lambda land_use_definition: ScagLandUse.objects.update_or_create( + key='scg_lu__' + slugify(land_use_definition.land_use_description).replace('-', ','), + defaults=dict( + land_use_definition=land_use_definition, + name=land_use_definition.land_use_description, + medium=update_or_create_built_form_medium( + 'scag_land_use_%s' % land_use_definition.land_use, + land_use_lookup.get(str(land_use_definition.land_use), None) + ) + ))[0], + ScagLandUseDefinition.objects.all()) + + return merge( + self.parent_fixture.built_forms(), + dict(scag_land_use=construct_scag_land_uses())) + + def tag_built_forms(self, built_forms_dict): + self.parent_fixture.tag_built_forms(built_forms_dict), + # Give client built_forms a default tag if they don't have any tag yet + Tag.objects.update_or_create(tag='Unsorted') + for built_form in built_forms_dict['scag_land_use']: + if built_form.tags.count() == 0: + tag, created, updated = Tag.objects.update_or_create( + tag=built_form.land_use_definition.land_use_type or 'Unsorted') + built_form.tags.add(tag) + + def built_form_sets(self): + + return self.parent_fixture.built_form_sets() + [dict( + key='scag_land_use', + name='Land Use:SCAG', + client='scag', + description='ScagLandUse containing only scag_land_uses', + clazz=ScagLandUse + )] diff --git a/footprint/client/configuration/scag/built_form/scag_land_use.py b/footprint/client/configuration/scag/built_form/scag_land_use.py new file mode 100644 index 000000000..6c03d8661 --- /dev/null +++ b/footprint/client/configuration/scag/built_form/scag_land_use.py @@ -0,0 +1,22 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.client.configuration.scag.built_form.scag_land_use_definition import ScagLandUseDefinition +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.built_form.client_land_use import ClientLandUse + +class ScagLandUse(ClientLandUse): + objects = GeoInheritanceManager() + land_use_definition = models.ForeignKey(ScagLandUseDefinition, null=False) + + class Meta(object): + app_label = 'main' diff --git a/footprint/client/configuration/scag/built_form/scag_land_use_definition.py b/footprint/client/configuration/scag/built_form/scag_land_use_definition.py new file mode 100644 index 000000000..d54def206 --- /dev/null +++ b/footprint/client/configuration/scag/built_form/scag_land_use_definition.py @@ -0,0 +1,35 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.built_form.client_land_use_definition import ClientLandUseDefinition + +__author__ = 'calthorpe_associates' + +from django.db import models + +class ScagLandUseDefinition(ClientLandUseDefinition): + objects = GeoInheritanceManager() + land_use_description = models.CharField(max_length=100, null=True, blank=True) + land_use_type = models.CharField(max_length=100, null=True, blank=True) + # The id imported from Scag + land_use = models.IntegerField(null=False) + + class Meta(object): + abstract = False + app_label = 'main' diff --git a/footprint/client/configuration/scag/built_form/scag_land_use_definitions.json b/footprint/client/configuration/scag/built_form/scag_land_use_definitions.json new file mode 100644 index 000000000..74795fa0c --- /dev/null +++ b/footprint/client/configuration/scag/built_form/scag_land_use_definitions.json @@ -0,0 +1,1190 @@ +[ + { + "fields": { + "land_use_description": "Urban or Built-Up", + "land_use_type": "Urban or Built-Up", + "land_use": 1000 + }, + "model": "main.scaglandusedefinition", + "pk": 1000 + }, + { + "fields": { + "land_use_description": "Residential", + "land_use_type": "Mixed Residential", + "land_use": 1100 + }, + "model": "main.scaglandusedefinition", + "pk": 1100 + }, + { + "fields": { + "land_use_description": "Single Family Residential", + "land_use_type": "Single Family Residential", + "land_use": 1110 + }, + "model": "main.scaglandusedefinition", + "pk": 1110 + }, + { + "fields": { + "land_use_description": "High-Density Single Family Residential", + "land_use_type": "Single Family Residential", + "land_use": 1111 + }, + "model": "main.scaglandusedefinition", + "pk": 1111 + }, + { + "fields": { + "land_use_description": "Low-Density Single Family Residential", + "land_use_type": "Single Family Residential", + "land_use": 1112 + }, + "model": "main.scaglandusedefinition", + "pk": 1112 + }, + { + "fields": { + "land_use_description": "Rural Residential", + "land_use_type": "Single Family Residential", + "land_use": 1113 + }, + "model": "main.scaglandusedefinition", + "pk": 1113 + }, + { + "fields": { + "land_use_description": "Multi-Family Residential (Medium-High Density Residential) ", + "land_use_type": "Multi-Family Residential", + "land_use": 1120 + }, + "model": "main.scaglandusedefinition", + "pk": 1120 + }, + { + "fields": { + "land_use_description": "Mixed Multi-Family Residential", + "land_use_type": "Multi-Family Residential", + "land_use": 1121 + }, + "model": "main.scaglandusedefinition", + "pk": 1121 + }, + { + "fields": { + "land_use_description": "Duplexes, Triplexes and 2- or 3-Unit Condominiums and Townhouses ", + "land_use_type": "Multi-Family Residential", + "land_use": 1122 + }, + "model": "main.scaglandusedefinition", + "pk": 1122 + }, + { + "fields": { + "land_use_description": "Low-Rise Apartments, Condominiums, and Townhouses", + "land_use_type": "Multi-Family Residential", + "land_use": 1123 + }, + "model": "main.scaglandusedefinition", + "pk": 1123 + }, + { + "fields": { + "land_use_description": "Medium-Rise Apartments and Condominiums ", + "land_use_type": "Multi-Family Residential", + "land_use": 1124 + }, + "model": "main.scaglandusedefinition", + "pk": 1124 + }, + { + "fields": { + "land_use_description": "High-Rise Apartments and Condominiums ", + "land_use_type": "Multi-Family Residential", + "land_use": 1125 + }, + "model": "main.scaglandusedefinition", + "pk": 1125 + }, + { + "fields": { + "land_use_description": "Mobile Homes and Trailer Parks", + "land_use_type": "Mobile Homes and Trailer Parks", + "land_use": 1130 + }, + "model": "main.scaglandusedefinition", + "pk": 1130 + }, + { + "fields": { + "land_use_description": "Trailer Parks and Mobile Home Courts, High-Density", + "land_use_type": "Mobile Homes and Trailer Parks", + "land_use": 1131 + }, + "model": "main.scaglandusedefinition", + "pk": 1131 + }, + { + "fields": { + "land_use_description": "Mobile Home Courts and Subdivisions, Low-Density", + "land_use_type": "Mobile Homes and Trailer Parks", + "land_use": 1132 + }, + "model": "main.scaglandusedefinition", + "pk": 1132 + }, + { + "fields": { + "land_use_description": "Mixed Residential (single and multiple residential)", + "land_use_type": "Mixed Residential", + "land_use": 1140 + }, + "model": "main.scaglandusedefinition", + "pk": 1140 + }, + { + "fields": { + "land_use_description": "Commercial and Services", + "land_use_type": "Commercial and Services", + "land_use": 1200 + }, + "model": "main.scaglandusedefinition", + "pk": 1200 + }, + { + "fields": { + "land_use_description": "General Office Use", + "land_use_type": "General Office", + "land_use": 1210 + }, + "model": "main.scaglandusedefinition", + "pk": 1210 + }, + { + "fields": { + "land_use_description": "Low- and Medium-Rise Major Office Use", + "land_use_type": "General Office", + "land_use": 1211 + }, + "model": "main.scaglandusedefinition", + "pk": 1211 + }, + { + "fields": { + "land_use_description": "High-Rise Major Office Use", + "land_use_type": "General Office", + "land_use": 1212 + }, + "model": "main.scaglandusedefinition", + "pk": 1212 + }, + { + "fields": { + "land_use_description": "Skyscrapers", + "land_use_type": "General Office", + "land_use": 1213 + }, + "model": "main.scaglandusedefinition", + "pk": 1213 + }, + { + "fields": { + "land_use_description": "Retail Stores and Commercial Services", + "land_use_type": "Commercial and Services", + "land_use": 1220 + }, + "model": "main.scaglandusedefinition", + "pk": 1220 + }, + { + "fields": { + "land_use_description": "Regional Shopping Center", + "land_use_type": "Commercial and Services", + "land_use": 1221 + }, + "model": "main.scaglandusedefinition", + "pk": 1221 + }, + { + "fields": { + "land_use_description": "Retail Centers (Non-Strip With Contiguous Interconnected Off-Street Parking)", + "land_use_type": "Commercial and Services", + "land_use": 1222 + }, + "model": "main.scaglandusedefinition", + "pk": 1222 + }, + { + "fields": { + "land_use_description": "Retail Strip Development", + "land_use_type": "Commercial and Services", + "land_use": 1223 + }, + "model": "main.scaglandusedefinition", + "pk": 1223 + }, + { + "fields": { + "land_use_description": "Other Commercial", + "land_use_type": "Commercial and Services", + "land_use": 1230 + }, + "model": "main.scaglandusedefinition", + "pk": 1230 + }, + { + "fields": { + "land_use_description": "Commercial Storage", + "land_use_type": "Commercial and Services", + "land_use": 1231 + }, + "model": "main.scaglandusedefinition", + "pk": 1231 + }, + { + "fields": { + "land_use_description": "Commercial Recreation", + "land_use_type": "Commercial and Services", + "land_use": 1232 + }, + "model": "main.scaglandusedefinition", + "pk": 1232 + }, + { + "fields": { + "land_use_description": "Hotels and Motels", + "land_use_type": "Commercial and Services", + "land_use": 1233 + }, + "model": "main.scaglandusedefinition", + "pk": 1233 + }, + { + "fields": { + "land_use_description": "Public Facilities", + "land_use_type": "Facilities", + "land_use": 1240 + }, + "model": "main.scaglandusedefinition", + "pk": 1240 + }, + { + "fields": { + "land_use_description": "Government Offices", + "land_use_type": "Facilities", + "land_use": 1241 + }, + "model": "main.scaglandusedefinition", + "pk": 1241 + }, + { + "fields": { + "land_use_description": "Police and Sheriff Stations", + "land_use_type": "Facilities", + "land_use": 1242 + }, + "model": "main.scaglandusedefinition", + "pk": 1242 + }, + { + "fields": { + "land_use_description": "Fire Stations", + "land_use_type": "Facilities", + "land_use": 1243 + }, + "model": "main.scaglandusedefinition", + "pk": 1243 + }, + { + "fields": { + "land_use_description": "Major Medical Health Care Facilities", + "land_use_type": "Facilities", + "land_use": 1244 + }, + "model": "main.scaglandusedefinition", + "pk": 1244 + }, + { + "fields": { + "land_use_description": "Religious Facilities", + "land_use_type": "Facilities", + "land_use": 1245 + }, + "model": "main.scaglandusedefinition", + "pk": 1245 + }, + { + "fields": { + "land_use_description": "Other Public Facilities", + "land_use_type": "Facilities", + "land_use": 1246 + }, + "model": "main.scaglandusedefinition", + "pk": 1246 + }, + { + "fields": { + "land_use_description": "Public Parking Facilities", + "land_use_type": "Facilities", + "land_use": 1247 + }, + "model": "main.scaglandusedefinition", + "pk": 1247 + }, + { + "fields": { + "land_use_description": "Special Use Facilities", + "land_use_type": "Facilities", + "land_use": 1250 + }, + "model": "main.scaglandusedefinition", + "pk": 1250 + }, + { + "fields": { + "land_use_description": "Correctional Facilities", + "land_use_type": "Facilities", + "land_use": 1251 + }, + "model": "main.scaglandusedefinition", + "pk": 1251 + }, + { + "fields": { + "land_use_description": "Special Care Facilities", + "land_use_type": "Facilities", + "land_use": 1252 + }, + "model": "main.scaglandusedefinition", + "pk": 1252 + }, + { + "fields": { + "land_use_description": "Other Special Use Facilities", + "land_use_type": "Facilities", + "land_use": 1253 + }, + "model": "main.scaglandusedefinition", + "pk": 1253 + }, + { + "fields": { + "land_use_description": "Educational Institutions", + "land_use_type": "Education", + "land_use": 1260 + }, + "model": "main.scaglandusedefinition", + "pk": 1260 + }, + { + "fields": { + "land_use_description": "Pre-Schools/Day Care Centers", + "land_use_type": "Education", + "land_use": 1261 + }, + "model": "main.scaglandusedefinition", + "pk": 1261 + }, + { + "fields": { + "land_use_description": "Elementary Schools", + "land_use_type": "Education", + "land_use": 1262 + }, + "model": "main.scaglandusedefinition", + "pk": 1262 + }, + { + "fields": { + "land_use_description": "Junior or Intermediate High Schools", + "land_use_type": "Education", + "land_use": 1263 + }, + "model": "main.scaglandusedefinition", + "pk": 1263 + }, + { + "fields": { + "land_use_description": "Senior High Schools", + "land_use_type": "Education", + "land_use": 1264 + }, + "model": "main.scaglandusedefinition", + "pk": 1264 + }, + { + "fields": { + "land_use_description": "Colleges and Universities", + "land_use_type": "Education", + "land_use": 1265 + }, + "model": "main.scaglandusedefinition", + "pk": 1265 + }, + { + "fields": { + "land_use_description": "Trade Schools and Professional Training Facilities", + "land_use_type": "Education", + "land_use": 1266 + }, + "model": "main.scaglandusedefinition", + "pk": 1266 + }, + { + "fields": { + "land_use_description": "Military Installations", + "land_use_type": "Military Installations", + "land_use": 1270 + }, + "model": "main.scaglandusedefinition", + "pk": 1270 + }, + { + "fields": { + "land_use_description": "Base (Built-up Area)", + "land_use_type": "Military Installations", + "land_use": 1271 + }, + "model": "main.scaglandusedefinition", + "pk": 1271 + }, + { + "fields": { + "land_use_description": "Vacant Area", + "land_use_type": "Military Installations", + "land_use": 1272 + }, + "model": "main.scaglandusedefinition", + "pk": 1272 + }, + { + "fields": { + "land_use_description": "Air Field", + "land_use_type": "Military Installations", + "land_use": 1273 + }, + "model": "main.scaglandusedefinition", + "pk": 1273 + }, + { + "fields": { + "land_use_description": "Former Base (Built-up Area)", + "land_use_type": "Military Installations", + "land_use": 1274 + }, + "model": "main.scaglandusedefinition", + "pk": 1274 + }, + { + "fields": { + "land_use_description": "Former Base Vacant Area", + "land_use_type": "Military Installations", + "land_use": 1275 + }, + "model": "main.scaglandusedefinition", + "pk": 1275 + }, + { + "fields": { + "land_use_description": "Former Base Air Field", + "land_use_type": "Military Installations", + "land_use": 1276 + }, + "model": "main.scaglandusedefinition", + "pk": 1276 + }, + { + "fields": { + "land_use_description": "Industrial", + "land_use_type": "Industrial", + "land_use": 1300 + }, + "model": "main.scaglandusedefinition", + "pk": 1300 + }, + { + "fields": { + "land_use_description": "Light Industrial", + "land_use_type": "Industrial", + "land_use": 1310 + }, + "model": "main.scaglandusedefinition", + "pk": 1310 + }, + { + "fields": { + "land_use_description": "Manufacturing, Assembly, and Industrial Services", + "land_use_type": "Industrial", + "land_use": 1311 + }, + "model": "main.scaglandusedefinition", + "pk": 1311 + }, + { + "fields": { + "land_use_description": "Picture and Television Production Lots", + "land_use_type": "Industrial", + "land_use": 1312 + }, + "model": "main.scaglandusedefinition", + "pk": 1312 + }, + { + "fields": { + "land_use_description": "Packing Houses and Grain Elevators", + "land_use_type": "Industrial", + "land_use": 1313 + }, + "model": "main.scaglandusedefinition", + "pk": 1313 + }, + { + "fields": { + "land_use_description": "Research and Development", + "land_use_type": "Industrial", + "land_use": 1314 + }, + "model": "main.scaglandusedefinition", + "pk": 1314 + }, + { + "fields": { + "land_use_description": "Heavy Industrial", + "land_use_type": "Industrial", + "land_use": 1320 + }, + "model": "main.scaglandusedefinition", + "pk": 1320 + }, + { + "fields": { + "land_use_description": "Manufacturing", + "land_use_type": "Industrial", + "land_use": 1321 + }, + "model": "main.scaglandusedefinition", + "pk": 1321 + }, + { + "fields": { + "land_use_description": "Petroleum Refining and Processing", + "land_use_type": "Industrial", + "land_use": 1322 + }, + "model": "main.scaglandusedefinition", + "pk": 1322 + }, + { + "fields": { + "land_use_description": "Open Storage", + "land_use_type": "Industrial", + "land_use": 1323 + }, + "model": "main.scaglandusedefinition", + "pk": 1323 + }, + { + "fields": { + "land_use_description": "Major Metal Processing", + "land_use_type": "Industrial", + "land_use": 1324 + }, + "model": "main.scaglandusedefinition", + "pk": 1324 + }, + { + "fields": { + "land_use_description": "Chemical Processing", + "land_use_type": "Industrial", + "land_use": 1325 + }, + "model": "main.scaglandusedefinition", + "pk": 1325 + }, + { + "fields": { + "land_use_description": "Extraction", + "land_use_type": "Industrial", + "land_use": 1330 + }, + "model": "main.scaglandusedefinition", + "pk": 1330 + }, + { + "fields": { + "land_use_description": "Mineral Extraction - Other Than Oil and Gas", + "land_use_type": "Industrial", + "land_use": 1331 + }, + "model": "main.scaglandusedefinition", + "pk": 1331 + }, + { + "fields": { + "land_use_description": "Mineral Extraction - Oil and Gas", + "land_use_type": "Industrial", + "land_use": 1332 + }, + "model": "main.scaglandusedefinition", + "pk": 1332 + }, + { + "fields": { + "land_use_description": "Wholesaling and Warehousing", + "land_use_type": "Industrial", + "land_use": 1340 + }, + "model": "main.scaglandusedefinition", + "pk": 1340 + }, + { + "fields": { + "land_use_description": "Transportation, Communications, and Utilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1400 + }, + "model": "main.scaglandusedefinition", + "pk": 1400 + }, + { + "fields": { + "land_use_description": "Transportation", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1410 + }, + "model": "main.scaglandusedefinition", + "pk": 1410 + }, + { + "fields": { + "land_use_description": "Airports", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1411 + }, + "model": "main.scaglandusedefinition", + "pk": 1411 + }, + { + "fields": { + "land_use_description": "Railroads", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1412 + }, + "model": "main.scaglandusedefinition", + "pk": 1412 + }, + { + "fields": { + "land_use_description": "Freeways and Major Roads", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1413 + }, + "model": "main.scaglandusedefinition", + "pk": 1413 + }, + { + "fields": { + "land_use_description": "Carpool and Rideshare Facilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1414 + }, + "model": "main.scaglandusedefinition", + "pk": 1414 + }, + { + "fields": { + "land_use_description": "Bus Terminals and Transit Centers", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1415 + }, + "model": "main.scaglandusedefinition", + "pk": 1415 + }, + { + "fields": { + "land_use_description": "Truck Terminals", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1416 + }, + "model": "main.scaglandusedefinition", + "pk": 1416 + }, + { + "fields": { + "land_use_description": "Harbor Facilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1417 + }, + "model": "main.scaglandusedefinition", + "pk": 1417 + }, + { + "fields": { + "land_use_description": "Navigation Aids", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1418 + }, + "model": "main.scaglandusedefinition", + "pk": 1418 + }, + { + "fields": { + "land_use_description": "Communication Facilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1420 + }, + "model": "main.scaglandusedefinition", + "pk": 1420 + }, + { + "fields": { + "land_use_description": "Utility Facilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1430 + }, + "model": "main.scaglandusedefinition", + "pk": 1430 + }, + { + "fields": { + "land_use_description": "Electrical Power Facilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1431 + }, + "model": "main.scaglandusedefinition", + "pk": 1431 + }, + { + "fields": { + "land_use_description": "Solid Waste Disposal Facilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1432 + }, + "model": "main.scaglandusedefinition", + "pk": 1432 + }, + { + "fields": { + "land_use_description": "Liquid Waste Disposal Facilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1433 + }, + "model": "main.scaglandusedefinition", + "pk": 1433 + }, + { + "fields": { + "land_use_description": "Water Storage Facilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1434 + }, + "model": "main.scaglandusedefinition", + "pk": 1434 + }, + { + "fields": { + "land_use_description": "Natural Gas and Petroleum Facilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1435 + }, + "model": "main.scaglandusedefinition", + "pk": 1435 + }, + { + "fields": { + "land_use_description": "Water Transfer Facilities ", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1436 + }, + "model": "main.scaglandusedefinition", + "pk": 1436 + }, + { + "fields": { + "land_use_description": "Improved Flood Waterways and Structures", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1437 + }, + "model": "main.scaglandusedefinition", + "pk": 1437 + }, + { + "fields": { + "land_use_description": "Mixed Utilities", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1438 + }, + "model": "main.scaglandusedefinition", + "pk": 1438 + }, + { + "fields": { + "land_use_description": "Maintenance Yards ", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1440 + }, + "model": "main.scaglandusedefinition", + "pk": 1440 + }, + { + "fields": { + "land_use_description": "Bus Yards", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1441 + }, + "model": "main.scaglandusedefinition", + "pk": 1441 + }, + { + "fields": { + "land_use_description": "Rail Yards", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1442 + }, + "model": "main.scaglandusedefinition", + "pk": 1442 + }, + { + "fields": { + "land_use_description": "Mixed Transportation", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1450 + }, + "model": "main.scaglandusedefinition", + "pk": 1450 + }, + { + "fields": { + "land_use_description": "Mixed Transportation and Utility", + "land_use_type": "Transportation, Communications, and Utilities", + "land_use": 1460 + }, + "model": "main.scaglandusedefinition", + "pk": 1460 + }, + { + "fields": { + "land_use_description": "Mixed Commercial and Industrial", + "land_use_type": "Mixed Commercial and Industrial", + "land_use": 1500 + }, + "model": "main.scaglandusedefinition", + "pk": 1500 + }, + { + "fields": { + "land_use_description": "Mixed Residential and Commercial Services", + "land_use_type": "Mixed Residential and Commercial", + "land_use": 1600 + }, + "model": "main.scaglandusedefinition", + "pk": 1600 + }, + { + "fields": { + "land_use_description": "Under Construction", + "land_use_type": "Under Construction", + "land_use": 1700 + }, + "model": "main.scaglandusedefinition", + "pk": 1700 + }, + { + "fields": { + "land_use_description": "Open Space and Recreation", + "land_use_type": "Open Space and Recreation", + "land_use": 1800 + }, + "model": "main.scaglandusedefinition", + "pk": 1800 + }, + { + "fields": { + "land_use_description": "Golf Courses ", + "land_use_type": "Open Space and Recreation", + "land_use": 1810 + }, + "model": "main.scaglandusedefinition", + "pk": 1810 + }, + { + "fields": { + "land_use_description": "Local Parks and Recreation", + "land_use_type": "Open Space and Recreation", + "land_use": 1820 + }, + "model": "main.scaglandusedefinition", + "pk": 1820 + }, + { + "fields": { + "land_use_description": "State and National Parks and Recreation", + "land_use_type": "Open Space and Recreation", + "land_use": 1830 + }, + "model": "main.scaglandusedefinition", + "pk": 1830 + }, + { + "fields": { + "land_use_description": "Cemeteries", + "land_use_type": "Open Space and Recreation", + "land_use": 1840 + }, + "model": "main.scaglandusedefinition", + "pk": 1840 + }, + { + "fields": { + "land_use_description": "Wildlife Preserves and Sanctuaries", + "land_use_type": "Open Space and Recreation", + "land_use": 1850 + }, + "model": "main.scaglandusedefinition", + "pk": 1850 + }, + { + "fields": { + "land_use_description": "Specimen Gardens and Arboreta", + "land_use_type": "Open Space and Recreation", + "land_use": 1860 + }, + "model": "main.scaglandusedefinition", + "pk": 1860 + }, + { + "fields": { + "land_use_description": "Beach Parks ", + "land_use_type": "Open Space and Recreation", + "land_use": 1870 + }, + "model": "main.scaglandusedefinition", + "pk": 1870 + }, + { + "fields": { + "land_use_description": "Other Open Space and Recreation ", + "land_use_type": "Open Space and Recreation", + "land_use": 1880 + }, + "model": "main.scaglandusedefinition", + "pk": 1880 + }, + { + "fields": { + "land_use_description": "Urban Vacant (developable)", + "land_use_type": "Vacant", + "land_use": 1900 + }, + "model": "main.scaglandusedefinition", + "pk": 1900 + }, + { + "fields": { + "land_use_description": "Agriculture", + "land_use_type": "Agriculture", + "land_use": 2000 + }, + "model": "main.scaglandusedefinition", + "pk": 2000 + }, + { + "fields": { + "land_use_description": "Cropland and Improved Pasture Land", + "land_use_type": "Agriculture", + "land_use": 2100 + }, + "model": "main.scaglandusedefinition", + "pk": 2100 + }, + { + "fields": { + "land_use_description": "Irrigated Cropland and Improved Pasture Land", + "land_use_type": "Agriculture", + "land_use": 2110 + }, + "model": "main.scaglandusedefinition", + "pk": 2110 + }, + { + "fields": { + "land_use_description": "Non-Irrigated Cropland and Improved Pasture Land", + "land_use_type": "Agriculture", + "land_use": 2120 + }, + "model": "main.scaglandusedefinition", + "pk": 2120 + }, + { + "fields": { + "land_use_description": "Orchards and Vineyards", + "land_use_type": "Agriculture", + "land_use": 2200 + }, + "model": "main.scaglandusedefinition", + "pk": 2200 + }, + { + "fields": { + "land_use_description": "Nurseries", + "land_use_type": "Agriculture", + "land_use": 2300 + }, + "model": "main.scaglandusedefinition", + "pk": 2300 + }, + { + "fields": { + "land_use_description": "Dairy, Intensive Livestock, and Associated Facilities", + "land_use_type": "Agriculture", + "land_use": 2400 + }, + "model": "main.scaglandusedefinition", + "pk": 2400 + }, + { + "fields": { + "land_use_description": "Poultry Operations", + "land_use_type": "Agriculture", + "land_use": 2500 + }, + "model": "main.scaglandusedefinition", + "pk": 2500 + }, + { + "fields": { + "land_use_description": "Other Agriculture", + "land_use_type": "Agriculture", + "land_use": 2600 + }, + "model": "main.scaglandusedefinition", + "pk": 2600 + }, + { + "fields": { + "land_use_description": "Horse Ranches", + "land_use_type": "Agriculture", + "land_use": 2700 + }, + "model": "main.scaglandusedefinition", + "pk": 2700 + }, + { + "fields": { + "land_use_description": "Vacant (developable)", + "land_use_type": "Vacant", + "land_use": 3000 + }, + "model": "main.scaglandusedefinition", + "pk": 3000 + }, + { + "fields": { + "land_use_description": "Vacant Undifferentiated", + "land_use_type": "Vacant", + "land_use": 3100 + }, + "model": "main.scaglandusedefinition", + "pk": 3100 + }, + { + "fields": { + "land_use_description": "Abandoned Orchards and Vineyards", + "land_use_type": "Vacant", + "land_use": 3200 + }, + "model": "main.scaglandusedefinition", + "pk": 3200 + }, + { + "fields": { + "land_use_description": "Vacant With Limited Improvements", + "land_use_type": "Vacant", + "land_use": 3300 + }, + "model": "main.scaglandusedefinition", + "pk": 3300 + }, + { + "fields": { + "land_use_description": "Beaches (vacant)", + "land_use_type": "Vacant", + "land_use": 3400 + }, + "model": "main.scaglandusedefinition", + "pk": 3400 + }, + { + "fields": { + "land_use_description": "Water", + "land_use_type": "Water", + "land_use": 4000 + }, + "model": "main.scaglandusedefinition", + "pk": 4000 + }, + { + "fields": { + "land_use_description": "Water, Undifferentiated", + "land_use_type": "Water", + "land_use": 4100 + }, + "model": "main.scaglandusedefinition", + "pk": 4100 + }, + { + "fields": { + "land_use_description": "Harbor Water Facilities", + "land_use_type": "Water", + "land_use": 4200 + }, + "model": "main.scaglandusedefinition", + "pk": 4200 + }, + { + "fields": { + "land_use_description": "Marina Water Facilities", + "land_use_type": "Water", + "land_use": 4300 + }, + "model": "main.scaglandusedefinition", + "pk": 4300 + }, + { + "fields": { + "land_use_description": "Water Within a Military Installation", + "land_use_type": "Water", + "land_use": 4400 + }, + "model": "main.scaglandusedefinition", + "pk": 4400 + }, + { + "fields": { + "land_use_description": "Area of Inundation (High Water) (1990 Database only)", + "land_use_type": "Water", + "land_use": 4500 + }, + "model": "main.scaglandusedefinition", + "pk": 4500 + }, + { + "fields": { + "land_use_description": "Undevelopable or protected Land ", + "land_use_type": "Undevelopable or Protected Land", + "land_use": 8888 + }, + "model": "main.scaglandusedefinition", + "pk": 8888 + }, + { + "fields": { + "land_use_description": "No Photo Coverage/Not in Update Study Area", + "land_use_type": "Unknown", + "land_use": 9999 + }, + "model": "main.scaglandusedefinition", + "pk": 9999 + } +] diff --git a/footprint/client/configuration/scag/config_entity/__init__.py b/footprint/client/configuration/scag/config_entity/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/scag/config_entity/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/scag/config_entity/scag_config_entities.py b/footprint/client/configuration/scag/config_entity/scag_config_entities.py new file mode 100644 index 000000000..2e29f4cfe --- /dev/null +++ b/footprint/client/configuration/scag/config_entity/scag_config_entities.py @@ -0,0 +1,78 @@ +from footprint.client.configuration.fixture import ConfigEntitiesFixture, MediumFixture +from footprint.client.configuration.default.config_entity.default_config_entities import ConfigEntityMediumKey +from django.conf import settings + +__author__ = 'calthorpe_associates' +from django.contrib.gis.geos import MultiPolygon, Polygon + + +class ScagConfigEntitiesFixture(ConfigEntitiesFixture): + def project_key(self): + return None + + def region_key(self): + return 'scag' + + def regions(self): + return [ + { + 'key': 'scag', + 'name': 'SCAG', + 'description': 'The SCAG region', + 'media': [ + MediumFixture(key=ConfigEntityMediumKey.Fab.ricate('scag_logo'), name='SCAG Logo', + url='/static/client/{0}/logos/scag.png'.format(CLIENT)) + ], + #defaulting to an Irvine view for the moment + 'bounds': MultiPolygon([Polygon(( + (-117.869537353516, 33.5993881225586), + (-117.869537353516, 33.7736549377441), + (-117.678024291992, 33.7736549377441), + (-117.678024291992, 33.5993881225586), + (-117.869537353516, 33.5993881225586), + ))]) + + }, + ] + + def projects(self, region=None): + return [ + { + 'key': 'irvine', + 'name': 'Irvine', + 'description': "City of Irvine", + 'base_year': 2012, + 'region_index': 0, + 'media': [ + MediumFixture(key=ConfigEntityMediumKey.Fab.ricate('irvine_logo'), name='Irvine Logo', + url='/static/client/{0}/logos/cityofirvine.png'.format(settings.CLIENT)) + ], + 'bounds': MultiPolygon([Polygon(( + (-117.869537353516, 33.5993881225586), + (-117.869537353516, 33.7736549377441), + (-117.678024291992, 33.7736549377441), + (-117.678024291992, 33.5993881225586), + (-117.869537353516, 33.5993881225586), + ))]) + }, + { + 'key': 'orange_county', + 'name': 'Orange County', + 'description': "City of Irvine", + 'base_year': 2012, + 'region_index': 0, + 'media': [], + 'bounds': MultiPolygon([Polygon(( + (-117.869537353516, 33.5993881225586), + (-117.869537353516, 33.7736549377441), + (-117.678024291992, 33.7736549377441), + (-117.678024291992, 33.5993881225586), + (-117.869537353516, 33.5993881225586), + ))]) + } + ] + + def scenarios(self, project=None, class_scope=None): + return self.matching_scope([], project_key=project.key if project else None, class_scope=class_scope) + + diff --git a/footprint/client/configuration/scag/config_entity/scag_project.py b/footprint/client/configuration/scag/config_entity/scag_project.py new file mode 100644 index 000000000..5f2496e26 --- /dev/null +++ b/footprint/client/configuration/scag/config_entity/scag_project.py @@ -0,0 +1,25 @@ + +from footprint.client.configuration.default.config_entity.default_project import DefaultProjectFixture +from footprint.main.lib.functions import merge + +__author__ = 'calthorpe_associates' + + +class ScagProjectFixture(DefaultProjectFixture): + + def feature_class_lookup(self): + """ + Adds mappings of custom Feature classes + :return: + """ + parent_fixture = self.parent_fixture + feature_class_lookup = parent_fixture.feature_class_lookup() + return merge(feature_class_lookup, {}) + + def default_db_entity_configurations(self): + """ + Project specific SCAG additional db_entities + :param default_dict: + :return: + """ + return super(ScagProjectFixture, self).default_db_entity_configurations() + [] diff --git a/footprint/client/configuration/scag/models/__init__.py b/footprint/client/configuration/scag/models/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/scag/models/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/scag/models/geographies/__init__.py b/footprint/client/configuration/scag/models/geographies/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/scag/models/geographies/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/scag/models/geographies/spz.py b/footprint/client/configuration/scag/models/geographies/spz.py new file mode 100644 index 000000000..d0160d6b4 --- /dev/null +++ b/footprint/client/configuration/scag/models/geographies/spz.py @@ -0,0 +1,10 @@ +__author__ = 'calthorpe_associates' + +from footprint.main.models.geospatial.feature import Feature + +class Spz(Feature): + """ + Represents an SCAG authoritative SPZ definition, whose pk is referenced by other geospatial tables + """ + class Meta(object): + app_label = 'main' diff --git a/footprint/client/configuration/scag/publishing/__init__.py b/footprint/client/configuration/scag/publishing/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/scag/publishing/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/scag/publishing/scag_land_use_symbology.py b/footprint/client/configuration/scag/publishing/scag_land_use_symbology.py new file mode 100644 index 000000000..b0255a000 --- /dev/null +++ b/footprint/client/configuration/scag/publishing/scag_land_use_symbology.py @@ -0,0 +1,144 @@ +from footprint.client.configuration.fixture import LandUseSymbologyFixture + +__author__ = 'calthorpe_associates' + + +class ScagLandUseSymbology(LandUseSymbologyFixture): + """ + a resource for translating SACOG's land uses into UF Built Forms + """ + def land_use_color_lookup(self): + return { + '1000': '#CD6666', + '1100': '#E3C400', + '1110': '#FFFF00', + '1111': '#FFFF00', + '1112': '#FFFF00', + '1113': '#FFFF00', + '1120': '#FFAA00', + '1121': '#FFAA00', + '1122': '#FFAA00', + '1123': '#FFAA00', + '1124': '#FFAA00', + '1125': '#FFAA00', + '1130': '#FFEBAA', + '1131': '#FFEBAA', + '1132': '#FFEBAA', + '1140': '#E3C400', + '1200': '#FF0000', + '1210': '#9C0000', + '1211': '#9C0000', + '1212': '#9C0000', + '1213': '#9C0000', + '1220': '#FF0000', + '1221': '#FF0000', + '1222': '#FF0000', + '1223': '#FF0000', + '1230': '#FF0000', + '1231': '#FF0000', + '1232': '#FF0000', + '1233': '#FF0000', + '1240': '#7A8EF5', + '1241': '#7A8EF5', + '1242': '#7A8EF5', + '1243': '#7A8EF5', + '1244': '#7A8EF5', + '1245': '#7A8EF5', + '1246': '#7A8EF5', + '1247': '#7A8EF5', + '1250': '#7A8EF5', + '1251': '#7A8EF5', + '1252': '#7A8EF5', + '1253': '#7A8EF5', + '1260': '#77B3D7', + '1261': '#77B3D7', + '1262': '#77B3D7', + '1263': '#77B3D7', + '1264': '#77B3D7', + '1265': '#77B3D7', + '1266': '#77B3D7', + '1270': '#8400A8', + '1271': '#8400A8', + '1272': '#8400A8', + '1273': '#8400A8', + '1274': '#8400A8', + '1275': '#8400A8', + '1276': '#8400A8', + '1300': '#0033CF', + '1310': '#0033CF', + '1311': '#0033CF', + '1312': '#0033CF', + '1313': '#0033CF', + '1314': '#0033CF', + '1320': '#0033CF', + '1321': '#0033CF', + '1322': '#0033CF', + '1323': '#0033CF', + '1324': '#0033CF', + '1325': '#0033CF', + '1330': '#0033CF', + '1331': '#0033CF', + '1332': '#0033CF', + '1340': '#0033CF', + '1400': '#9C9C9C', + '1410': '#9C9C9C', + '1411': '#9C9C9C', + '1412': '#9C9C9C', + '1413': '#9C9C9C', + '1414': '#9C9C9C', + '1415': '#9C9C9C', + '1416': '#9C9C9C', + '1417': '#9C9C9C', + '1418': '#9C9C9C', + '1420': '#9C9C9C', + '1430': '#9C9C9C', + '1431': '#9C9C9C', + '1432': '#9C9C9C', + '1433': '#9C9C9C', + '1434': '#9C9C9C', + '1435': '#9C9C9C', + '1436': '#9C9C9C', + '1437': '#9C9C9C', + '1438': '#9C9C9C', + '1440': '#9C9C9C', + '1441': '#9C9C9C', + '1442': '#9C9C9C', + '1450': '#9C9C9C', + '1460': '#9C9C9C', + '1500': '#DF73FF', + '1600': '#A87000', + '1700': '#595959', + '1800': '#4CE600', + '1810': '#4CE600', + '1820': '#4CE600', + '1830': '#4CE600', + '1840': '#4CE600', + '1850': '#4CE600', + '1860': '#4CE600', + '1870': '#4CE600', + '1880': '#4CE600', + '1900': '#A8A800', + '2000': '#267300', + '2100': '#267300', + '2110': '#267300', + '2120': '#267300', + '2200': '#267300', + '2300': '#267300', + '2400': '#267300', + '2500': '#267300', + '2600': '#267300', + '2700': '#267300', + '3000': '#A8A800', + '3100': '#A8A800', + '3200': '#A8A800', + '3300': '#A8A800', + '3400': '#A8A800', + '4000': '#BEFFE8', + '4100': '#BEFFE8', + '4200': '#BEFFE8', + '4300': '#BEFFE8', + '4400': '#BEFFE8', + '4500': '#BEFFE8', + '8888': '#D1FF73', + '9999': '#CCCCCC' + } diff --git a/footprint/client/configuration/scag/publishing/scag_layer.py b/footprint/client/configuration/scag/publishing/scag_layer.py new file mode 100644 index 000000000..b5908d67d --- /dev/null +++ b/footprint/client/configuration/scag/publishing/scag_layer.py @@ -0,0 +1,17 @@ +from footprint.client.configuration.fixture import LayerConfigurationFixture + + +__author__ = 'calthorpe_associates' + + +class ScagLayerConfigurationFixtures(LayerConfigurationFixture): + def layer_libraries(self, layers=None): + return self.parent_fixture.layer_libraries( + self.matching_scope( + layers or self.layers, + class_scope=self.config_entity and self.config_entity.__class__)) + + def layers(self): + return self.parent_fixture.layers() + [ + + ] \ No newline at end of file diff --git a/footprint/client/configuration/scag/publishing/scag_result.py b/footprint/client/configuration/scag/publishing/scag_result.py new file mode 100644 index 000000000..3fc612a06 --- /dev/null +++ b/footprint/client/configuration/scag/publishing/scag_result.py @@ -0,0 +1,56 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.client.configuration.fixture import ResultConfigurationFixture +from footprint.main.publishing.result_initialization import ResultConfiguration, ResultLibraryKey, ResultKey, ResultSort +from footprint.main.models.config.scenario import BaseScenario +from footprint.main.models.keys.keys import Keys + + +class ScagResultConfigurationFixtures(ResultConfigurationFixture): + def results(self): + """ + Used to update or create Results per ConfigEntity instance + Returns the result library(ies) scoped for the class of self.config_entity. + The Result will belong to the ResultLibrary specified by result_library_key + :return: + """ + return self.matching_scope( + # Basic Core result query that summarizes increments + self.parent_fixture.results() + [ + # Aggregate result from the Analytic Bars + ResultConfiguration( + class_scope=BaseScenario, + result_type='analytic_bars', + result_library_key=ResultLibraryKey.DEFAULT, + source_db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE, + result_db_entity_key=ResultKey.Fab.ricate('base_bars'), + + name='Base Results', + attributes=['population', 'employment', 'dwelling_units'], + db_column_lookup=dict( + population='pop', + employment='emp', + dwelling_units='du', + ), + extent_lookup=dict( + population=dict(min=-25000, max=25000), + dwelling_units=dict(min=-2500, max=2500), + employment=dict(min=-2500, max=2500), + ), + labels=['Population', 'Employees', 'Dwelling Units'], + stackable=False, + create_query=self.simple_aggregate, + sort_priority=ResultSort.BASE + ) + ], + class_scope=self.config_entity and self.config_entity.__class__) + diff --git a/footprint/client/configuration/scag/scag_init.py b/footprint/client/configuration/scag/scag_init.py new file mode 100644 index 000000000..261ae68b4 --- /dev/null +++ b/footprint/client/configuration/scag/scag_init.py @@ -0,0 +1,32 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.client.configuration.fixture import InitFixture + +class ScagInitFixture(InitFixture): + def import_database(self): + return dict( + host='10.0.0.133', + database='scag_pilot', + user='footprint', + password='[PASSWORD]') + + + def model_class_modules(self): + """ + SCAG defines additional concrete model classes in the following modules + :return: + """ + return [ + ("built_form", "land_use_definition"), + ("built_form", "land_use") + ] diff --git a/footprint/client/configuration/scag__irvine/__init__.py b/footprint/client/configuration/scag__irvine/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/scag__irvine/base/__init__.py b/footprint/client/configuration/scag__irvine/base/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/scag__irvine/base/scag_existing_land_use_parcel_feature.py b/footprint/client/configuration/scag__irvine/base/scag_existing_land_use_parcel_feature.py new file mode 100644 index 000000000..bb288b489 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_existing_land_use_parcel_feature.py @@ -0,0 +1,53 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class ScagExistingLandUseParcelFeature(Feature): + + scaguid12 = models.IntegerField(null=False) + parcel_id = models.CharField(max_length=100, null=True, blank=True) + city = models.CharField(max_length=100, null=True, blank=True) + county = models.CharField(max_length=100, null=True, blank=True) + apn = models.CharField(max_length=100, null=True, blank=True) + acres = models.DecimalField(max_digits=10, decimal_places=3) + lot_square_feet = models.IntegerField(null=True) + building_square_feet = models.IntegerField(null=True) + #land_use_definition = models.ForeignKey(ScagLandUseDefinition, null=True) + land_use = models.IntegerField(null=True) + land_use_description = models.CharField(max_length=100, null=True, blank=True) + land_use_type = models.CharField(max_length=100, null=True, blank=True) + comments = models.CharField(max_length=200, null=True, default=None) + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateScagExisitingLandUseParcelFeature(ScagExistingLandUseParcelFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/scag__irvine/base/scag_floodplain_feature.py b/footprint/client/configuration/scag__irvine/base/scag_floodplain_feature.py new file mode 100644 index 000000000..d986fbd3c --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_floodplain_feature.py @@ -0,0 +1,28 @@ +from django.contrib.gis.db import models + +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class ScagFloodplainFeature(Feature): + zone = models.CharField(max_length=50, null=False) + cobra = models.CharField(max_length=50, null=False) + special_flood_hazard_area = models.CharField(max_length=50, null=False) + symbol = models.IntegerField(null=False) + panel_type = models.CharField(max_length=50, null=False) + + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateScagFloodplainFeature(ScagFloodplainFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/scag__irvine/base/scag_general_plan_parcel_feature.py b/footprint/client/configuration/scag__irvine/base/scag_general_plan_parcel_feature.py new file mode 100644 index 000000000..f75a3b1ad --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_general_plan_parcel_feature.py @@ -0,0 +1,46 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class ScagGeneralPlanParcelFeature(Feature): + apn = models.CharField(max_length=100, null=True, blank=True) + # TODO this seems to have been removed + #land_use_definition = models.ForeignKey(ScagLandUseDefinition, null=True) + scag_general_plan_code = models.IntegerField(null=True) + general_plan_code = models.CharField(max_length=100, null=True, blank=True) + zone_code = models.CharField(max_length=100, null=True, blank=True) + comments = models.CharField(max_length=200, null=True, default=None) + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateScagGeneralPlanParcelFeature(ScagGeneralPlanParcelFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/scag__irvine/base/scag_habitat_conservation_areas_feature.py b/footprint/client/configuration/scag__irvine/base/scag_habitat_conservation_areas_feature.py new file mode 100644 index 000000000..e45d38998 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_habitat_conservation_areas_feature.py @@ -0,0 +1,26 @@ +from django.contrib.gis.db import models + +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class ScagHabitatConservationAreasFeature(Feature): + name = models.CharField(max_length=50, null=False) + hcp = models.CharField(max_length=50, null=False) + nccp = models.CharField(max_length=50, null=False) + stage = models.CharField(max_length=50, null=False) + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateScagHabitatConservationAreasFeature(ScagHabitatConservationAreasFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/scag__irvine/base/scag_jurisdiction_boundary_feature.py b/footprint/client/configuration/scag__irvine/base/scag_jurisdiction_boundary_feature.py new file mode 100644 index 000000000..f9c8ee09e --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_jurisdiction_boundary_feature.py @@ -0,0 +1,21 @@ +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class ScagJurisdictionBoundaryFeature(Feature): + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateScagJurisdictionBoundaryFeature(ScagJurisdictionBoundaryFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False + + diff --git a/footprint/client/configuration/scag__irvine/base/scag_parks_open_space_feature.py b/footprint/client/configuration/scag__irvine/base/scag_parks_open_space_feature.py new file mode 100644 index 000000000..bdea99149 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_parks_open_space_feature.py @@ -0,0 +1,28 @@ +from django.contrib.gis.db import models + +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class ScagParksOpenSpaceFeature(Feature): + site_name = models.CharField(max_length=100, null=False) + agency = models.CharField(max_length=50, null=False) + access = models.CharField(max_length=50, null=False) + land_use_designation = models.CharField(max_length=50, null=False) + acres = models.DecimalField(max_digits=10, decimal_places=3) + + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateScagParksOpenSpaceFeature(ScagParksOpenSpaceFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/scag__irvine/base/scag_primary_spz_feature.py b/footprint/client/configuration/scag__irvine/base/scag_primary_spz_feature.py new file mode 100644 index 000000000..88657a12b --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_primary_spz_feature.py @@ -0,0 +1,46 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models + +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class ScagPrimarySPZFeature(Feature): + spzid = models.CharField(max_length=30, null=False) + acres = models.DecimalField(max_digits=10, decimal_places=3) + population = models.IntegerField(null=False) + households = models.IntegerField(null=False) + employees = models.IntegerField(null=False) + comments = models.CharField(max_length=200, null=True, default=None) + + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateScagPrimarySPZFeature(ScagPrimarySPZFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False diff --git a/footprint/client/configuration/scag__irvine/base/scag_sphere_of_influence_feature.py b/footprint/client/configuration/scag__irvine/base/scag_sphere_of_influence_feature.py new file mode 100644 index 000000000..cca894801 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_sphere_of_influence_feature.py @@ -0,0 +1,19 @@ +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class ScagSphereOfInfluenceFeature(Feature): + class Meta(object): + abstract = True + app_label = 'main' + + +class TemplateScagSphereOfInfluenceFeature(ScagSphereOfInfluenceFeature): + """ + Template subclass so that south generates migrations that we can apply to the dynamically generated subclasses + """ + + class Meta(object): + app_label = 'main' + abstract = False + diff --git a/footprint/client/configuration/scag__irvine/base/scag_tier1_taz_feature.py b/footprint/client/configuration/scag__irvine/base/scag_tier1_taz_feature.py new file mode 100644 index 000000000..6bac243a2 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_tier1_taz_feature.py @@ -0,0 +1,14 @@ +from django.contrib.gis.db import models + +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class ScagTier1TazFeature(Feature): + id_taz12a = models.CharField(max_length=50, null=False) + taz_id = models.IntegerField(null=False) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/client/configuration/scag__irvine/base/scag_tier2_taz_feature.py b/footprint/client/configuration/scag__irvine/base/scag_tier2_taz_feature.py new file mode 100644 index 000000000..509f84009 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/base/scag_tier2_taz_feature.py @@ -0,0 +1,15 @@ +from django.contrib.gis.db import models + +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class ScagTier2TazFeature(Feature): + id_taz12a = models.CharField(max_length=50, null=False) + id_taz12b = models.CharField(max_length=50, null=False) + taz_id = models.IntegerField(null=False) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/client/configuration/scag__irvine/config_entity/__init__.py b/footprint/client/configuration/scag__irvine/config_entity/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/config_entity/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/scag__irvine/config_entity/scag__irvine_config_entities.py b/footprint/client/configuration/scag__irvine/config_entity/scag__irvine_config_entities.py new file mode 100644 index 000000000..da208477e --- /dev/null +++ b/footprint/client/configuration/scag__irvine/config_entity/scag__irvine_config_entities.py @@ -0,0 +1,27 @@ +from footprint.client.configuration.fixture import ConfigEntitiesFixture +from footprint.main.models.category import Category +from footprint.main.models.config.scenario import BaseScenario + + +__author__ = 'calthorpe_associates' + + + +class ScagIrvineConfigEntitiesFixture(ConfigEntitiesFixture): + + + def scenarios(self, project=None, class_scope=None): + + parent_fixture = self.parent_fixture + return self.matching_scope(parent_fixture.scenarios(project) + [ + { + 'class_scope': BaseScenario, + 'key': '{0}_base'.format(project.key.split('_')[0]), + 'name': '{0} Base'.format(project.name), + 'description': 'Base year parcel review and editing {0}'.format(project.name), + 'year': 2012, + 'selections': dict(built_form_sets='scag_land_use'), + 'categories': [Category(key='category', value='base_year')] + }], project_key=project.key if project else None, class_scope=class_scope) + + diff --git a/footprint/client/configuration/scag__irvine/config_entity/scag__irvine_project.py b/footprint/client/configuration/scag__irvine/config_entity/scag__irvine_project.py new file mode 100644 index 000000000..50eb19503 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/config_entity/scag__irvine_project.py @@ -0,0 +1,112 @@ +from django.db import models +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration, db_entity_key_to_feature_class_lookup +from footprint.client.configuration.fixture import ProjectFixture +from footprint.client.configuration.scag.base.scag_transit_areas_feature import ScagTransitAreasFeature +from footprint.client.configuration.scag__irvine.base.scag_general_plan_parcel_feature import ScagGeneralPlanParcelFeature +from footprint.client.configuration.scag__irvine.base.scag_primary_spz_feature import ScagPrimarySPZFeature +from footprint.client.configuration.scag__irvine.base.scag_jurisdiction_boundary_feature import ScagJurisdictionBoundaryFeature +from footprint.client.configuration.scag__irvine.base.scag_sphere_of_influence_feature import ScagSphereOfInfluenceFeature +from footprint.client.configuration.scag__irvine.base.scag_floodplain_feature import ScagFloodplainFeature +from footprint.client.configuration.scag__irvine.base.scag_tier1_taz_feature import ScagTier1TazFeature +from footprint.client.configuration.scag__irvine.base.scag_tier2_taz_feature import ScagTier2TazFeature +from footprint.client.configuration.scag__irvine.base.scag_parks_open_space_feature import ScagParksOpenSpaceFeature +from footprint.client.configuration.scag__irvine.base.scag_existing_land_use_parcel_feature import ScagExistingLandUseParcelFeature +from footprint.main.lib.functions import merge +from footprint.main.models import ConfigEntity +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +class ScagIrvineProjectFixture(ProjectFixture): + + def default_db_entity_configurations(self): + """ + Project specific SCAG additional db_entities + :param default_dict: + :return: + """ + + parent_fixture = self.parent_fixture + defaults = parent_fixture.default_db_entity_configurations() + config_entity = self.config_entity + + return super(ScagIrvineProjectFixture, self).default_db_entity_configurations() + [ + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_SCAG_EXISTING_LAND_USE_PARCEL_SOURCE, + base_class=ScagExistingLandUseParcelFeature, + primary_geography=True, + related_fields=dict( + land_use_definition=dict( + single=True, + related_class_name='footprint.client.configuration.sacog.built_form.scag_land_use_definition.ScagLandUseDefinition', + related_class_join_field_name='land_use', + source_class_join_field_name='land_use'), + ), + census_blocks=dict( + single=True, + related_db_entity_key=Keys.DB_ABSTRACT_CENSUS_BLOCK, + related_class_join_field_name='block', + source_class_join_field_name='census_block', + ) + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_GENERAL_PLAN_FEATURE, + base_class=ScagGeneralPlanParcelFeature, + intersection=dict(type='polygon', to='centroid'), + related_fields=dict( + land_use_definition=dict( + single=True, + related_class_name='footprint.client.configuration.sacog.built_form.scag_land_use_definition.ScagLandUseDefinition', + related_class_join_field_name='land_use', + source_class_join_field_name='scag_general_plan_code')) + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_PRIMARY_SPZ_SOURCE, + base_class=ScagPrimarySPZFeature, + primary_geography=True + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_JURISDICTION_BOUNDARY, + base_class=ScagJurisdictionBoundaryFeature, + intersection=dict(type='polygon', to='centroid') + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_SPHERE_OF_INFLUENCE, + base_class=ScagSphereOfInfluenceFeature, + intersection=dict(type='polygon', to='centroid') + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_FLOODPLAIN, + base_class=ScagFloodplainFeature, + intersection=dict(type='polygon') + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_TIER1_TAZ, + base_class=ScagTier1TazFeature, + intersection=dict(type='polygon', to='centroid') + ), + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_TIER2_TAZ, + base_class=ScagTier2TazFeature, + intersection=dict(type='polygon', to='centroid') + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_PARKS_OPEN_SPACE, + base_class=ScagParksOpenSpaceFeature, + intersection=dict(type='polygon') + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_TRANSIT_AREAS, + base_class=ScagTransitAreasFeature, + intersection=dict(type='polygon', to='centroid') + ) + + ] diff --git a/footprint/client/configuration/scag__irvine/publishing/__init__.py b/footprint/client/configuration/scag__irvine/publishing/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/publishing/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/scag__irvine/publishing/scag__irvine_layer.py b/footprint/client/configuration/scag__irvine/publishing/scag__irvine_layer.py new file mode 100644 index 000000000..57db42ede --- /dev/null +++ b/footprint/client/configuration/scag__irvine/publishing/scag__irvine_layer.py @@ -0,0 +1,98 @@ +from footprint.client.configuration.fixture import LayerConfigurationFixture +from footprint.client.configuration.mixins.publishing.layer_primary_base import primary_base_template_context_dict +from footprint.client.configuration.scag__irvine.base.scag_general_plan_parcel_feature import ScagGeneralPlanParcelFeature +from footprint.client.configuration.scag__irvine.base.scag_existing_land_use_parcel_feature import ScagExistingLandUseParcelFeature +from footprint.main.models.config.scenario import BaseScenario, Scenario +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.presentation_configuration import LayerConfiguration + +__author__ = 'calthorpe_associates' + + +class ScagIrvineLayerConfigurationFixtures(LayerConfigurationFixture): + def layer_libraries(self, layers=None): + + return self.parent_fixture.layer_libraries(layers or self.layers()) + + def layers(self): + + return self.parent_fixture.layers() + [ + LayerConfiguration( + scope=BaseScenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_SCAG_EXISTING_LAND_USE_PARCEL_SOURCE, + visible=False, + visible_attributes=['land_use_definition__id'], + column_alias_lookup=dict(land_use_definition__id='land_use_defintion_id'), + #built_form_set_key='scag_land_use', + template_context_dict=primary_base_template_context_dict(ScagExistingLandUseParcelFeature) + ), + LayerConfiguration( + scope=BaseScenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_GENERAL_PLAN_FEATURE, + visible=False, + visible_attributes=['land_use_definition__id'], + column_alias_lookup=dict(land_use_definition__id='land_use_defintion_id'), + #built_form_set_key='scag_land_use', + template_context_dict=primary_base_template_context_dict(ScagGeneralPlanParcelFeature) + ), + LayerConfiguration( + scope=BaseScenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_PRIMARY_SPZ_SOURCE, + visible=True, + visible_attributes=['spzid'], + template_context_dict={'attributes': {'spzid': {'unstyled': True}}} + ), + # The following are scoped for both Scenario subclasses + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_JURISDICTION_BOUNDARY, + visible=True, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_SPHERE_OF_INFLUENCE, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_FLOODPLAIN, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_TIER1_TAZ, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_TIER2_TAZ, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_PARKS_OPEN_SPACE, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_TRANSIT_AREAS, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + ] + + def import_layer_configurations(self): + return self.parent_fixture.import_layer_configurations() diff --git a/footprint/client/configuration/scag__irvine/resource/__init__.py b/footprint/client/configuration/scag__irvine/resource/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/resource/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/scag__irvine/resource/scag__irvine_resource.py b/footprint/client/configuration/scag__irvine/resource/scag__irvine_resource.py new file mode 100644 index 000000000..14be021c4 --- /dev/null +++ b/footprint/client/configuration/scag__irvine/resource/scag__irvine_resource.py @@ -0,0 +1,97 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie.fields import ToOneField +from footprint.client.configuration.scag__irvine.base.scag_primary_spz_feature import ScagPrimarySPZFeature +from footprint.client.configuration.scag__irvine.base.scag_general_plan_parcel_feature import ScagGeneralPlanParcelFeature +from footprint.client.configuration.scag__irvine.base.scag_existing_land_use_parcel_feature import ScagExistingLandUseParcelFeature +from footprint.main.resources.client.client_land_use_definition_resource import ClientLandUseDefinitionResource +from footprint.main.resources.feature_resources import FeatureResource +from footprint.main.resources.geography_resource import GeographyResource +from footprint.main.utils.dynamic_subclassing import get_dynamic_resource_class + + +class ScagExistingLandUseParcelFeatureResource(FeatureResource): + + """ + Resource representing SCAG's Existing Land Use parcel features + """ + + # TODO dynamically create this resource field specific to the client's LandUseDefinitionResource + #land_use_definiton = ToOneField(ClientLandUseDefinitionResource, 'land_use_definiton', full=False, null=True) + + geography = ToOneField(GeographyResource, 'geography', full=True) + + def dynamic_resource_class(self, params, feature_class): + land_use_definition_resource_class = ClientLandUseDefinitionResource().create_subclass(params) + return get_dynamic_resource_class( + self.__class__, + feature_class, + land_use_definition=ToOneField(land_use_definition_resource_class, 'land_use_definition', full=True, + null=True) + ) + + class Meta(FeatureResource.Meta): + abstract = True + # Override the limited fields of the FeatureResource to allow all fields through + fields = [] + # Except for these fields + excludes = FeatureResource.Meta.excludes + resource_name = 'scag_existing_land_use_parcel' + queryset = ScagExistingLandUseParcelFeature.objects.all() # Just for model_class initialization, should never be called + + +class ScagSpzFeatureResource(FeatureResource): + """ + Resource representing SCAG SPZ + """ + geography = ToOneField(GeographyResource, 'geography', full=True) + + def dynamic_resource_class(self, params, feature_class): + return get_dynamic_resource_class( + self.__class__, + feature_class, + ) + + class Meta(FeatureResource.Meta): + abstract = True + # Override the limited fields of the FeatureResource to allow all fields through + fields = [] + # Except for these fields + excludes = FeatureResource.Meta.excludes + resource_name = 'scag_primary_spz_feature' + queryset = ScagPrimarySPZFeature.objects.all() # Just for model_class initialization, should never be called + + +class ScagGeneralPlanParcelFeatureResource(FeatureResource): + geography = ToOneField(GeographyResource, 'geography', full=True) + + def dynamic_resource_class(self, params, feature_class): + + land_use_definition_resource_class = ClientLandUseDefinitionResource().create_subclass(params) + return get_dynamic_resource_class( + self.__class__, + feature_class, + land_use_definition=ToOneField(land_use_definition_resource_class, + 'land_use_definition', + full=True, + null=True) + ) + + class Meta(FeatureResource.Meta): + abstract = True + # Override the limited fields of the FeatureResource to allow all fields through + fields = [] + # Except for these fields + excludes = FeatureResource.Meta.excludes + resource_name = 'scag_general_plan_parcel_feature' + queryset = ScagGeneralPlanParcelFeature.objects.all() # Just for model_class initialization, should never be called + diff --git a/footprint/client/configuration/scag__orange_county/__init__.py b/footprint/client/configuration/scag__orange_county/__init__.py new file mode 100644 index 000000000..505a9ec57 --- /dev/null +++ b/footprint/client/configuration/scag__orange_county/__init__.py @@ -0,0 +1,11 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com \ No newline at end of file diff --git a/footprint/client/configuration/scag__orange_county/config_entity/__init__.py b/footprint/client/configuration/scag__orange_county/config_entity/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/scag__orange_county/config_entity/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/scag__orange_county/config_entity/scag__orange_county_config_entities.py b/footprint/client/configuration/scag__orange_county/config_entity/scag__orange_county_config_entities.py new file mode 100644 index 000000000..a976643e6 --- /dev/null +++ b/footprint/client/configuration/scag__orange_county/config_entity/scag__orange_county_config_entities.py @@ -0,0 +1,46 @@ +from footprint.client.configuration.fixture import ConfigEntitiesFixture +from footprint.main.models.category import Category +from footprint.main.models.config.scenario import BaseScenario, FutureScenario + + +__author__ = 'calthorpe_associates' + + +class ScagOrangeCountyConfigEntitiesFixture(ConfigEntitiesFixture): + + + def scenarios(self, project=None, class_scope=None): + + parent_fixture = self.parent_fixture + return self.matching_scope(parent_fixture.scenarios(project) + [ + { + 'class_scope': BaseScenario, + 'key': '{0}_base'.format(project.key.split('_')[0]), + 'name': '{0}'.format(project.name), + 'description': 'Base year data review and editing', + 'year': 2012, + 'selections': dict(built_form_sets='scag_land_use'), + 'categories': [Category(key='category', value='base_year')] + }, + { + 'class_scope': FutureScenario, + 'key': '{0}_scenario_a'.format(project.key), + 'scope': project.schema(), + 'name': 'Scenario A', + 'description': 'Future Scenario for {0}'.format(project.name), + 'year': 2050, + 'selections': dict(), + 'categories': [Category(key='category', value='Future')] + }, + { + 'class_scope': FutureScenario, + 'key': '{0}_scenario_b'.format(project.key), + 'scope': project.schema(), + 'name': 'Scenario B', + 'description': 'Future Scenario for {0}'.format(project.name), + 'year': 2050, + 'selections': dict(), + 'categories': [Category(key='category', value='Future')] + }], project_key=project.key if project else None, class_scope=class_scope) + + diff --git a/footprint/client/configuration/scag__orange_county/config_entity/scag__orange_county_project.py b/footprint/client/configuration/scag__orange_county/config_entity/scag__orange_county_project.py new file mode 100644 index 000000000..afde9021f --- /dev/null +++ b/footprint/client/configuration/scag__orange_county/config_entity/scag__orange_county_project.py @@ -0,0 +1,61 @@ +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.client.configuration.fixture import ProjectFixture +from footprint.client.configuration.scag.base.scag_transit_areas_feature import ScagTransitAreasFeature +from footprint.main.lib.functions import merge +from footprint.main.models.base.base_parcel_feature import BaseParcelFeature +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +class ScagOrangeCountyProjectFixture(ProjectFixture): + def feature_class_lookup(self): + """ + Adds mappings of custom Feature classes + :return: + """ + parent_fixture = self.parent_fixture + feature_class_lookup = parent_fixture.feature_class_lookup() + return merge( + feature_class_lookup, + FeatureClassCreator.db_entity_key_to_feature_class_lookup(self.config_entity, self.default_db_entity_configurations()) + ) + + def default_db_entity_configurations(self): + """ + Project specific SCAG additional db_entities + :return: + """ + + config_entity = self.config_entity + + return super(ScagOrangeCountyProjectFixture, self).default_db_entity_configurations( + overrides={ + Keys.DB_ABSTRACT_BASE_FEATURE: dict( + # Override the name of the BaseFeature DbEntity to distinguish it from the parcel version below + name='Base SPZ Feature', + # Override the parcel class + geography_class_name='footprint.client.configuration.scag.models.geographies.spz.Spz' + ) + } + ) + [ + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_BASE_PARCEL_FEATURE, + base_class=BaseParcelFeature, + intersection=dict(type='centroid', to='polygon'), + geography_class_name='footprint.main.models.geographies.parcel.Parcel', + related_fields=dict( + built_form=dict( + single=True, + related_class_name='footprint.main.models.built_form.built_form.BuiltForm', + related_class_join_field_name='key', + source_class_join_field_name='built_form')) + ), + + create_db_entity_configuration(config_entity, + key=Keys.DB_ABSTRACT_TRANSIT_AREAS, + base_class=ScagTransitAreasFeature, + intersection=dict(type='polygon', to='centroid') + ) + ] diff --git a/footprint/client/configuration/scag__orange_county/publishing/__init__.py b/footprint/client/configuration/scag__orange_county/publishing/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/client/configuration/scag__orange_county/publishing/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/client/configuration/scag__orange_county/publishing/scag__orange_county_layer.py b/footprint/client/configuration/scag__orange_county/publishing/scag__orange_county_layer.py new file mode 100644 index 000000000..ffb7e0c5d --- /dev/null +++ b/footprint/client/configuration/scag__orange_county/publishing/scag__orange_county_layer.py @@ -0,0 +1,39 @@ +from footprint.client.configuration.fixture import LayerConfigurationFixture +from footprint.client.configuration.default.publishing.default_layer import built_form_template_context_dict +from footprint.main.publishing.layer_initialization import LayerLibraryKey, LayerTag, LayerSort +from footprint.main.models import Scenario +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.presentation_configuration import LayerConfiguration +from footprint.main.models.tag import Tag + +__author__ = 'calthorpe_associates' + + + +class ScagOrangeCountyLayerConfigurationFixtures(LayerConfigurationFixture): + def layer_libraries(self, layers=None): + return self.parent_fixture.layer_libraries(self.layers()) + + def layers(self): + return self.parent_fixture.layers() + [ + LayerConfiguration( + scope=Scenario.__name__, + layer_library_key=LayerLibraryKey.DEFAULT, + db_entity_key=Keys.DB_ABSTRACT_BASE_PARCEL_FEATURE, + visible=False, + visible_attributes=['builtforms__builtform_id'], + tags=[Tag.objects.get(tag=LayerTag.DEFAULT)], + template_context_dict=built_form_template_context_dict(), + sort_priority=LayerSort.BASE + ), + LayerConfiguration( + scope=Scenario.__name__, + db_entity_key=Keys.DB_ABSTRACT_TRANSIT_AREAS, + visible=False, + visible_attributes=['wkb_geometry'], + template_context_dict={'attributes': {'wkb_geometry': {'unstyled': True}}} + ), + ] + + def import_layer_configurations(self): + return self.parent_fixture.import_layer_configurations() diff --git a/footprint/client/configuration/utils.py b/footprint/client/configuration/utils.py new file mode 100644 index 000000000..affbe636f --- /dev/null +++ b/footprint/client/configuration/utils.py @@ -0,0 +1,118 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +import inspect +from django.utils import importlib +import sys +from footprint import settings + + +def form_module_name(module, module_fragment, schema): + return '%s.%s.%s_%s' % (schema, module, schema, module_fragment) if module else \ + '%s.%s_%s' % (schema, schema, module_fragment) + + +def resolve_client_module(module, module_fragment, schema=settings.CLIENT): + """ + Resolve the client module under footprint.client.configuration.[client], which client is the + specified or default client + :param module: The base module. This can be set to none to fetch a module directly under [client] + :param module_fragment: The sub module under base module (if base is not None) minus the client name. + So 'land_use_definition' resolves to '[client]_land_use_definition' + :param schema: Optional schema string. Defaults to settings.CLIENT + :return: + """ + module_string = form_module_name(module, module_fragment, schema) + + return importlib.import_module("footprint.client.configuration.%s" % module_string) + + +def resolve_default_fixture(module, module_fragment, fixture_class, *args, **kwargs): + """ + Return the default fixture_class version of the given module. The default_fixture is the fixture matching + the module [module].default_[module_fragment]. This is the top of the food-chain. All client fixtures + should incorporate the default fixtures into their own. The default also is used if a certain client + fixture is not defined. + :param module: module under footprint.client.configuration.default + :param module_fragment: the * fragment of the module default_* (e.g. 'built_form' for default_built_form) + :param fixture_class: The class to lookup in the module. Any subclass will match as well + :return: + """ + default_module_string = '%s.%s_%s' % (module, 'default', module_fragment) if module else \ + '%s_%s' % ('default', module_fragment) + client_module = importlib.import_module( + "footprint.client.configuration.default.%s" % default_module_string) + class_members = inspect.getmembers(sys.modules[client_module.__name__], inspect.isclass) + for name, cls in class_members: + if issubclass(cls, fixture_class) and cls != fixture_class: + return cls('global', *args, **kwargs) + +def resolve_parent_fixture(module, module_fragment, fixture_class, schema, *args, **kwargs): + parent_schema = '__'.join(schema.split('__')[0:-1]) + if parent_schema: + fixture = resolve_fixture(module, module_fragment, fixture_class, parent_schema, *args, **kwargs) + else: + # Otherwise fetch the default version + fixture = resolve_default_fixture(module, module_fragment, fixture_class, *args, **kwargs) + if not fixture: + raise Exception("Fixture in null. This should never happen: module:%s, module_fragment:%s, fixture_class:%s, schema:%s" % + (module, module_fragment, fixture_class, schema)) + return fixture + +def resolve_fixture(module, module_fragment, fixture_class, schema=settings.CLIENT, *args, **kwargs): + """ + Resolves an optional client-specific class located by the module_string relative to the module + "client.configuration.[schema]" where schema is the matching ConfigEntity.schema(). + As of now the settings.CLIENT is equivalent to a region schema, so this is the default value of schema + :param module; The directory name under the [schema] directory. Example: module='built_form' and + module_fragment='built_form' will resolve to the module built_form.[schema].built_form. If module is set + to None, then a top-level module will be returned. Example: module=None and module_fragment='init' will reolve + to module '[schema]_init'. + :param module_fragment: The non-schema fragment part of the module name (e.g. [schema]_config_entity => 'config_entity') + :param fixture_class: The class to lookup in the module. Any subclass will match as well + :param client: Optional client. Defaults to settings.CLIENT + :param schema: Optional config_entity schema string to winnow in on a more specif fixture class + :param args: Optional args to pass to the fixture class constructor + :param kwargs: Optional args to pass to the fixture class constructor + :return: Returns a matching subclass if one exists, otherwise the default version + """ + if schema: + try: + client_fixture_module = resolve_client_module(module, module_fragment, schema) + class_members = inspect.getmembers(sys.modules[client_fixture_module.__name__], inspect.isclass) + for name, cls in class_members: + if issubclass(cls, fixture_class) and cls != fixture_class: + return cls(schema, *args, **kwargs) + except ImportError, e: + # If nothing is found the default is returned below + if not e.message == 'No module named %s' % form_module_name(module, module_fragment, schema) and \ + not e.message == 'No module named %s' % form_module_name(module, module_fragment, schema).split('.')[-1]: + raise e + + # We didn't find a module for the given schema. Try the parent schema or default if we are already at region scope + return resolve_parent_fixture(module, module_fragment, fixture_class, schema, *args, **kwargs) + +def resolve_fixture_class(module, module_fragment, base_class, schema=settings.CLIENT): + """ + Resolves a subclass specific to a client + :param module: Module under footprint.main.fixtures.client.[client] + :param module_fragment: the class module name under module + :param base_class: The base class + :param schema: Scopes the fixture to a certain config_entity.schema(). Default is settings.CLIENT (= the region) + :return: + """ + if schema: + client_project = resolve_client_module(module, module_fragment, schema) + class_members = inspect.getmembers(sys.modules[client_project.__name__], inspect.isclass) + for name, cls in class_members: + if issubclass(cls, base_class) and cls != base_class: + return cls + raise Exception("Fixture class does not exist") diff --git a/footprint/common/__init__.py b/footprint/common/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/footprint/common/__init__.py @@ -0,0 +1 @@ + diff --git a/footprint/common/management/__init__.py b/footprint/common/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/common/management/commands/__init__.py b/footprint/common/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/common/management/commands/create_datadump.py b/footprint/common/management/commands/create_datadump.py new file mode 100644 index 000000000..5dea551c7 --- /dev/null +++ b/footprint/common/management/commands/create_datadump.py @@ -0,0 +1,85 @@ +import subprocess +import shlex +import os +from distutils import spawn +from optparse import make_option + +from django.core.management.base import BaseCommand, CommandError +from django.conf import settings + +from footprint.common.utils.postgres_utils import build_postgres_conn_string, postgres_env_password_loaded + + +class Command(BaseCommand): + args = ' (optional - if not specified use settings.py option)' + help = 'Creates a data dump' + + # I hate having to use optparse. We should be using argparse. + # When https://code.djangoproject.com/ticket/19973 gets fixed, we can + # use the new way of parsing (which will likely use argparse instead). + # In the meantime we'll stick with the documented way of doing this + option_list = BaseCommand.option_list + ( + make_option('--destination-folder', + action='store', + type='string', + dest='destination_folder', + default=getattr(settings, 'CALTHORPE_DATA_DUMP_LOCATION', ''), + help='output folder for daily dump'), + ) + + def handle(self, *args, **options): + + rsync = spawn.find_executable('rsync') + if rsync is None: + raise CommandError('rsync not found') + + pg_dump = spawn.find_executable('pg_dump') + if pg_dump is None: + raise CommandError('pg_dump not found') + + if options['destination_folder'] == '': + raise CommandError('--destination-folder not specified in command line nor settings.py') + + # make sure destination folder exists + if not os.path.exists(options['destination_folder']): + os.makedirs(options['destination_folder']) + + pg_output_file_name = os.path.join(options['destination_folder'], 'pg_dump.dmp') + media_output_copy_folder = os.path.join(options['destination_folder'], 'media') + + # make sure destination daily media folder also exists + if not os.path.exists(media_output_copy_folder): + os.makedirs(media_output_copy_folder) + + ################# + #rsync folder + rsync += ' -rapthzv {extra} {src} {dest}'.format(extra=settings.CALTHORPE_DAILY_DUMP_RSYNC_EXTRA_PARAMS, + src=settings.MEDIA_ROOT, + dest=media_output_copy_folder) + self.stdout.write(rsync + '\n') + + output = self.exec_cmd(rsync) + self.stdout.write(output) + + ################# + #do database dump + print settings.DATABASES['default'] + with postgres_env_password_loaded(settings.DATABASES['default']): + + pg_dump += ' {pg_conn_string} -Fc -f {output_file_name}'.format( + pg_conn_string=build_postgres_conn_string(settings.DATABASES['default']), + output_file_name=pg_output_file_name) + + output = self.exec_cmd(pg_dump) + self.stdout.write(output) + self.stdout.write('Wrote ' + pg_output_file_name + '\n') + + + def exec_cmd(self, cmd): + p = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + out, err = p.communicate() + + if p.returncode != 0: + raise CommandError('Error Executing "{cmd}\n{output}\n"'.format(cmd=cmd, output=out)) + + return out diff --git a/footprint/common/utils/__init__.py b/footprint/common/utils/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/common/utils/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/common/utils/async.py b/footprint/common/utils/async.py new file mode 100644 index 000000000..6e3ed52af --- /dev/null +++ b/footprint/common/utils/async.py @@ -0,0 +1,38 @@ +import logging +import re +from django.contrib.auth.models import User +import sys +from tastypie.models import ApiKey +from footprint.main.models import Job + +__author__ = 'calthorpe' +logger = logging.getLogger(__name__) + +def start_and_track_task(celery_task, api_key, *args, **kwargs): + user_id = ApiKey.objects.get(key=api_key).user_id + + job = Job.objects.create( + type=re.split(r'\.', celery_task.name)[-1], + status="New", + user=User.objects.get(id=user_id) + ) + job.save() + job = Job.objects.get(hashid=job.hashid) + + current_task = celery_task.apply_async( + args=list((job,) + args), + kwargs=kwargs, + soft_time_limit=3600, + time_limit=3600, + countdown=1 + ) + + if isinstance(current_task.result, Exception): + # This only works if Celery is set to eager + logger.error("Celery Task Error: %s", current_task.result, exc_info=1) + raise Exception("Celery Task Error: %s. Traceback: %s" % (current_task.result, job.data)), None, sys.exc_info()[2] + + job.task_id = current_task.id + job.save() + + return job \ No newline at end of file diff --git a/footprint/common/utils/postgres_utils.py b/footprint/common/utils/postgres_utils.py new file mode 100644 index 000000000..7dd7ded6f --- /dev/null +++ b/footprint/common/utils/postgres_utils.py @@ -0,0 +1,52 @@ +from contextlib import contextmanager +import os + +def build_postgres_conn_string(db_settings, omit_db=False): + ''' + Builds a postgres connection string based on the settings + of a particular database (e.g settings.DATABASES['default']) + ''' + + # We may want to ommit port & host depending on how pg_hba.conf has been configured + # (TCP sockets vs unix sockets). Specifying the port/host triggers a different authentication mechanism + + pg_conn_string = '' + + if len(db_settings['PORT']) > 0: + pg_conn_string += '-p {port} '.format(port=db_settings['PORT']) + + if len(db_settings['HOST']) > 0: + pg_conn_string += '-h {host} '.format(host=db_settings['HOST']) + + if len(db_settings['USER']) > 0: + pg_conn_string += '-U {user} '.format(user=db_settings['USER']) + + if not omit_db: + pg_conn_string += '{dbname}'.format(dbname=db_settings['NAME']) + + return pg_conn_string + +def pg_connection_parameters(db_settings): + return dict(database=db_settings['NAME'], host=db_settings['HOST'], user=db_settings['USER'], port=db_settings.get('PORT', 5432), password=db_settings['PASSWORD']) + +@contextmanager +def postgres_env_password_loaded(db_settings): + ''' + Sets postgres environment password to environment variable + upon entry and unsets it on exit if it did not exist + ''' + var_existed = False + + if 'PGPASSWORD' in os.environ: + old_val = os.environ['PGPASSWORD'] + var_existed = True + + os.environ["PGPASSWORD"] = db_settings['PASSWORD'] + + try: + yield + finally: + if var_existed: + os.environ['PGPASSWORD'] = old_val + else: + del os.environ['PGPASSWORD'] diff --git a/footprint/common/utils/websockets.py b/footprint/common/utils/websockets.py new file mode 100644 index 000000000..5060973e4 --- /dev/null +++ b/footprint/common/utils/websockets.py @@ -0,0 +1,15 @@ +from django.conf import settings +from django.utils import simplejson + +import redis + +def send_message_to_client(userid, message_dictionary): + """ + Sends a message to the web client through websockets + """ + r = redis.StrictRedis(host=settings.CELERY_REDIS_HOST, port=settings.CELERY_REDIS_PORT, db=settings.CELERY_REDIS_DB) + channel = 'channel_{0}'.format(userid) + json_message = simplejson.dumps(message_dictionary) + r.publish(channel, json_message) + + diff --git a/footprint/common/utils/zip_geodatabase.py b/footprint/common/utils/zip_geodatabase.py new file mode 100644 index 000000000..10eeee285 --- /dev/null +++ b/footprint/common/utils/zip_geodatabase.py @@ -0,0 +1,37 @@ + +'''Script: Sample_Zip_Geodatabase.py + +Purpose: The purpose of this script is to show the basics of using python to zip up an ESRI file geodatabase. +The + +Original Post: http://nodedangles.wordpress.com/2011/02/08/zipping-a-file-geodatabase-using-python/ +''' + +import os +import zipfile +import glob + + +def zip_file_gdb(path_to_file_gdb, path_to_zip=None, overwrite=True): + if not (os.path.exists(path_to_file_gdb)): + return False + + if not path_to_zip: + path_to_zip = path_to_file_gdb + ".zip" + + if os.path.exists(path_to_zip): + if overwrite: + os.remove(path_to_zip) + else: + raise Exception(path_to_zip + " exists, and you specified overwrite=False") + + + zipobj = zipfile.ZipFile(path_to_zip, 'w') + + for infile in glob.glob(path_to_file_gdb+"/*"): + zipobj.write(infile, os.path.basename(path_to_file_gdb)+"/"+os.path.basename(infile), zipfile.ZIP_DEFLATED) + print ("Zipping: "+infile) + + zipobj.close() + + return path_to_zip diff --git a/footprint/local_settings.py.default b/footprint/local_settings.py.default new file mode 100644 index 000000000..d20c952ef --- /dev/null +++ b/footprint/local_settings.py.default @@ -0,0 +1,107 @@ +import os + +# Fixtures for the given client will be loaded +CLIENT = 'sacog' + +USE_LOCAL_SAMPLE_DATA_SETS = True +USE_SAMPLE_DATA_SETS = True + +# Run celery as the main process +CELERY_ALWAYS_EAGER = False + +# Indicates that the system should use test data for default data sets +import warnings +warnings.filterwarnings( + 'error', r"DateTimeField received a naive datetime", + RuntimeWarning, r'django\.db\.models\.fields') + +DEBUG = True +TASTYPIE_FULL_DEBUG = True +CONSOLE_DEBUG = DEBUG +TEMPLATE_DEBUG = DEBUG + +REUSE_DB = 1 + +# Set the source for built form imports, or don't import them at all +IMPORT_BUILT_FORMS = 'CSV' # set to 'CSV' to run full import, 'JSON' to use fixtures, or 'FALSE' to skip import +# Skip slow calculations for testing +SKIP_ALL_BUILT_FORMS = False +TEST_SKIP_BUILT_FORM_COMPUTATIONS = False + +MEDIA_ROOT = '/srv/calthorpe_media' + +DATABASES = {'default': { + 'ENGINE': 'django.contrib.gis.db.backends.postgis', + 'OPTIONS': { + 'autocommit': True, + }, + 'NAME': 'urbanfootprint', + 'USER': 'calthorpe', + 'PASSWORD': '[PASSWORD]', + 'HOST': 'localhost', + 'PORT': '5432' + }, + 'sample_data': dict( + ENGINE='django.contrib.gis.db.backends.postgis', + HOST='localhost', + NAME='sample_data', + USER='calthorpe', + PASSWORD='[PASSWORD]', + PORT='5432' + ) + } + + +SQL_PATH = "/srv/calthorpe/urbanfootprint/calthorpe/server/footprint/main/static/sql" + + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' + }, + 'simple': { + 'format': '%(levelname)s %(message)s' + }, + }, + 'filters': { + }, + 'handlers': { + 'null': { + 'level': 'DEBUG', + 'class': 'django.utils.log.NullHandler', + }, + 'console': { + 'level': 'DEBUG', + #'class': 'logging.StreamHandler', + 'class': 'footprint.main.color_logger.ColorHandler', + 'formatter': 'simple' + } + }, + 'loggers': { + 'celery': { + 'handlers': ['console'], + 'level': 'DEBUG', + }, + 'footprint': { + 'handlers': ['console'], + 'propagate': True, + 'level': 'DEBUG', + }, + 'django': { + 'handlers': ['null'], + 'propagate': False, + 'level': 'INFO', + }, + 'django.db.backends': { + 'handlers': ['null'], # Quiet by default! + 'propagate': False, + 'level': 'DEBUG', + }, + } +} +from logging.config import dictConfig +dictConfig(LOGGING) + diff --git a/footprint/main/__init__.py b/footprint/main/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/admin.py b/footprint/main/admin.py new file mode 100644 index 000000000..cf77f08fe --- /dev/null +++ b/footprint/main/admin.py @@ -0,0 +1,21 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.contrib import admin +from footprint.main.models import Scenario +from .models.config.region import Region +from .models.config.project import Project + +admin.site.register(Region) +admin.site.register(Project) +admin.site.register(Scenario) + diff --git a/footprint/main/color_logger.py b/footprint/main/color_logger.py new file mode 100644 index 000000000..94623c01f --- /dev/null +++ b/footprint/main/color_logger.py @@ -0,0 +1,70 @@ +__author__ = 'calthorpe' + +import sys +import logging + + +# Source: http://xsnippet.org/359377/ +class _AnsiColorizer(object): + """ + A colorizer is an object that loosely wraps around a stream, allowing + callers to write text to the stream in a particular color. + + Colorizer classes must implement C{supported()} and C{write(text, color)}. + """ + _colors = dict(black=30, red=31, green=32, yellow=33, + blue=34, magenta=35, cyan=36, white=37) + + def __init__(self, stream): + self.stream = stream + + @classmethod + def supported(cls, stream=sys.stdout): + """ + A class method that returns True if the current platform supports + coloring terminal output using this method. Returns False otherwise. + """ + if not stream.isatty(): + return False # auto color only on TTYs + try: + import curses + except ImportError: + return False + else: + try: + try: + return curses.tigetnum("colors") > 2 + except curses.error: + curses.setupterm() + return curses.tigetnum("colors") > 2 + except: + raise + # guess false in case of error + return False + + def write(self, text, color): + """ + Write the given text to the stream in the given color. + + @param text: Text to be written to the stream. + + @param color: A string label for a color. e.g. 'red', 'white'. + """ + color = self._colors[color] + self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text)) + + +class ColorHandler(logging.StreamHandler): + def __init__(self, stream=sys.stdout): + super(ColorHandler, self).__init__(_AnsiColorizer(stream)) + + def emit(self, record): + msg_colors = { + logging.DEBUG: "green", + logging.INFO: "blue", + logging.WARNING: "yellow", + logging.ERROR: "red" + } + + color = msg_colors.get(record.levelno, "blue") + self.stream.write(record.msg + "\n", color) diff --git a/footprint/main/database/__init__.py b/footprint/main/database/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/database/import_data.py b/footprint/main/database/import_data.py new file mode 100755 index 000000000..20ce6304e --- /dev/null +++ b/footprint/main/database/import_data.py @@ -0,0 +1,293 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +import datetime +import logging +from os import path +import re +import stat +import os +from celery.utils.functional import uniq +from django.db import connections, connection, transaction + +import psycopg2 +from django.conf import settings +from footprint.common.utils.postgres_utils import pg_connection_parameters, build_postgres_conn_string + +from footprint.main.lib.functions import if_cond_raise, map_dict, filter_dict +from footprint.main.models.database.information_schema import InformationSchema, sync_geometry_columns, verify_srid + +# from calthorpe.main.publishing.geoserver import validate_geoserver_configuration +from footprint.main.utils.uf_toolbox import drop_table +from footprint.main.utils.utils import database_settings, execute_piped_with_stdin, execute_with_stdin, chop_geom, postgres_url_to_connection_dict, file_url_to_path, os_user +from django.conf import settings + +GEOMETRY_COLUMN = 'wkb_geometry' + +logger = logging.getLogger(__name__) + +# See Footprimporter manager command for options +class ImportData(object): + + def create_target_db_string(self): + self.target_database_connection = "-h {0} -p {1} --user {2} {3}".format( + self.target_database['HOST'], + self.target_database['PORT'] or 5432, + self.target_database['USER'], + self.target_database['NAME']) + logger.info("Using target database connection: {0}".format(self.target_database_connection)) + + def __init__(self, **arguments): + + self.arguments = arguments + self.dump_only = self.arguments.get('dump_only', None) + self.region_key = self.arguments.get('schema', None) + self.target_database = database_settings('default') + # The config_entity whose feature tables should be imported + self.config_entity = self.arguments.get('config_entity', None) + if self.config_entity: + logger.info("Importing config_entity {0}".format(self.config_entity)) + # The optional db_entity_key whose Feature class table should be imported. Otherwise all DbEntity tables + # are imported for the config_entity, including inherited ones from parent ConfigEntities + self.db_entity_key = self.arguments.get('db_entity_key', None) + self.db_entities = filter(lambda db_entity: not self.db_entity_key or (db_entity.key == self.db_entity_key), + self.config_entity.owned_db_entities()) + self.test = self.arguments.get('test', None) + + # The psql connection to the target server, normally the django server + self.create_target_db_string() + + self.command_execution = CommandExecution(logger) + + self.connections = ["{host}:*:*:{user}:{password}".format(**dict( + host=self.target_database['HOST'], + user=self.target_database['USER'], + password=self.target_database['PASSWORD']))] + + for db_entity in self.db_entities: + # Create a password file in order to avoid dealing with stdin for passwords + # This has been bypassed in favor of passing the password to stdin + if not (db_entity.has_db_url or db_entity.has_file_url): + raise Exception("This db_entity, {0}, has no database or file url".format(db_entity.key)) + if db_entity.has_db_url: + # Setup the connection strings for the db_entity so that we can get around interactive password authentication + # TODO someone should clean this up to do it a better way + connection_dict = postgres_url_to_connection_dict(db_entity.url) + self.connections.append("{host}:*:*:{user}:{password}".format(**connection_dict)) + + def run(self): + """ + Imports the data and syncs dependent system components, such as GeoServer to the imported data. + """ + + self.passwordfile = path.join('/tmp/pgpassword_%s' % os_user()) + f = open(self.passwordfile, 'w') + for connection in uniq(self.connections): + f.write("{0}\n".format(connection)) + os.fchmod(f.fileno(), stat.S_IRUSR | stat.S_IWUSR) + + f.close() + # Set the ENV variable to use this file + os.environ['PGPASSFILE'] = self.passwordfile + logger.info("Created password file at %s" % self.passwordfile) + + logger.info("Importing data") + self.import_data() + + os.remove(self.passwordfile) + del os.environ['PGPASSFILE'] + + def import_data(self, **kwargs): + """ + Imports data from an external source to create the test data + :return a two item tuple containing the region that was imported and a list of the imported projects + """ + + # Calculate a sample lat/lon box of the config_entity + config_entity = self.config_entity + if self.test: + bounds = chop_geom(config_entity.bounds, 0.90) + logger.info(u"Creating subselection with extents: {0}. This will be used to crop any table that doesn't have a sample version".format(bounds)) + + for db_entity in self.db_entities: + + if db_entity.has_file_url: + # Remove the temp public table from public if exists from a prior run + if re.match('file://(?P.+)/(?P.+).shp', db_entity.url): + drop_table(re.match('file://(?P.+)/(?P.+).shp', db_entity.url).groupdict()['filename']) + shape_file_path = file_url_to_path(db_entity.url) + # Create a command that pipes shp2pgsql to psql + logger.debug("verifying SRID {0}".format(db_entity.srid)) + srid = verify_srid(db_entity.srid) + shp_to_psql_command = '/usr/lib/postgresql/9.1/bin/shp2pgsql -s {srid} -g wkb_geometry -I {shapefile_path}'.format( + shapefile_path=shape_file_path, srid=srid.srid) + ' | /usr/bin/psql {0} -q'.format(self.target_database_connection) + results = self.command_execution.run(shp_to_psql_command, + pipe_commands=False, + stdin="{0}\n{1}".format(self.arguments.get('password', None), self.target_database.get('PASSWORD', None))) + + generated_name = os.path.splitext(os.path.basename(shape_file_path))[0] + move_to_schema = "alter table public.{1} set schema {0};".format(db_entity.schema, generated_name) + rename = "alter table {0}.{1} rename to {2};".format(db_entity.schema, generated_name, db_entity.key) + spatial_index = '''create index {0}_{1}_geom_idx on {0}.{1} using GIST (wkb_geometry);'''.format(db_entity.schema, db_entity.key) + drop_constraint = '''alter table {0}.{1} drop constraint enforce_srid_wkb_geometry'''.format(db_entity.schema, db_entity.key) + reproject = '''update {0}.{1} set wkb_geometry = st_SetSRID(st_transform(wkb_geometry, 4326),4326)'''.format(db_entity.schema, db_entity.key) + + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + logger.debug("Moving import shapefile table to schema: %s" % move_to_schema) + cursor.execute(move_to_schema) + logger.debug("Renaming shapefile table: %s" % rename) + cursor.execute(rename) + cursor.execute(spatial_index) + cursor.execute(drop_constraint) + cursor.execute(reproject) + + # Remove the temp public table from public if exists after the run + drop_table(re.match('file://(?P.+)/(?P.+).shp', db_entity.url).groupdict()['filename']) + elif db_entity.has_db_url: + # For now we only import data for DbEntity instances with a configured database url + connection_dict = postgres_url_to_connection_dict(db_entity.url) + # The import database currently stores tables as public.[config_entity.key]_[feature_class._meta.db_table (with schema removed)][_sample (for samples)] + # We always use the table name without the word sample for the target table name + source_table = "{0}_{1}_{2}".format(config_entity.key, db_entity.table, 'sample') if settings.USE_SAMPLE_DATA_SETS or self.test else "{0}_{1}".format(config_entity.key, db_entity.table) + table = db_entity.table + self._dump_tables_to_target('-t %s' % source_table, source_schema='public', target_schema=db_entity.schema, source_table=source_table, target_table=table, connection_dict=connection_dict) + + if db_entity.has_file_url or db_entity.has_db_url: + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + transform_to_4326 = '''update "{schema}"."{table}" + set wkb_geometry = st_setSRID(st_transform(wkb_geometry, 4326), 4326);'''.format + cursor.execute(transform_to_4326(schema=db_entity.schema, table=db_entity.table)) + # If we imported the table successfully + if InformationSchema.objects.table_exists(db_entity.schema, db_entity.table): + # Tell PostGIS about the new geometry column or the table + sync_geometry_columns(db_entity.schema, db_entity.table) + + def _dump_tables_to_target(self, table_selector, source_schema=None, target_schema=None, source_table=None, target_table=None, connection_dict=None, predicate_table_fetcher=lambda: []): + """ + Dumps the table indicated by the table_selector string to the target_schema on the target database. Throws an exception if the target already exists + :param table_selector: A string used in pg_dump to specify the table, such as '-t TABLE_NAME'. Optional attributes like --schema-only may be included + :param source_schema: The optional source_schema to use when specifying a target_schema. Both can be left null or both most be used. + :param target_schema: The optional target schema on the target database whither to write the table. If None the schema is that of the source + :param source_table optional table name to use when specifying a target_table. Both can be left null or both most be used. + :param target_table optional table name to write the table. If None the table is that of the host + :param connection_dict: The optional database configuration. A dict of user, password, host, port, and database. Defaults to self.pg_dump_connection + :param predicate_table_fetcher: A lambda that returns multiple results to indicate the target already exists, and an exception should be raised. + :return: True if the table was created. + """ + def sed_format(str, required_variables, **kwargs): + str = str.replace(' ', '\ ') + str = "sed " + str + return str.format(source_table=source_table, + source_schema=source_schema, + target_table=target_table, + target_schema=target_schema) if all(required_variables) else None + + # We optionally use sed to change the name of the schema and or table + sed_table = sed_format('s/{source_table}/{target_table}/g', [source_table, target_table]) + # We include the table here so public is only changed where associated with the table + sed_schema = sed_format('s/{source_schema}\.{target_table}/{target_schema}\.{target_table}/g', [source_schema, target_schema]) + # Also add the schema to the create table statement + sed_schema_table_create = sed_format("s/CREATE TABLE {target_table}/CREATE TABLE {target_schema}.{target_table}/g", [source_schema, target_schema]) + sed_schema_table_copy = sed_format("s/COPY {target_table}/COPY {target_schema}.{target_table}/g", [source_schema, target_schema]) + sed_schema_table_alter = sed_format("s/ALTER TABLE ONLY {target_table}/ALTER TABLE ONLY {target_schema}.{target_table}/g", [source_schema, target_schema]) + + pg_dump_connection = "--host={host} --port={port} --user={user} {database}".format(**connection_dict) + dump_to_psql_command = ' | '.join(filter(lambda str: str, [ + '/usr/bin/pg_dump {0} {1}'.format(pg_dump_connection, table_selector), + sed_table, + sed_schema, + sed_schema_table_create, + sed_schema_table_copy, + sed_schema_table_alter, + '/usr/bin/psql {0}'.format(self.target_database_connection) + ])) + target_table_already_exists = len(predicate_table_fetcher()) > 0 + results = self.command_execution.run(dump_to_psql_command, + predicate=lambda: not target_table_already_exists, + validator=Validator( + predicate_table_fetcher(), + lambda results: len(results) > 0, + lambda results: Exception('{0} tables already exist: {1}'.format( + len(results), + map(lambda result: result.table_name, results)))), + stdin="{0}\n{1}".format(self.arguments.get('password', None), self.target_database.get('PASSWORD', None)) + ) + return not target_table_already_exists + +class CommandExecution: + + def __init__(self, logger): + logger = logger + + def _exec_piped(self, commands, stdin): + logger.info("Running %s", ' | ' .join(map(lambda command: ' '.join(command), commands))) + out_and_err_tuple = execute_piped_with_stdin(commands, stdin) + if out_and_err_tuple[0]: + logger.info(out_and_err_tuple[0]) + if out_and_err_tuple[1]: + logger.warn(out_and_err_tuple[1]) + return out_and_err_tuple + + def _exec(self, command, stdin): + logger.info("Running %s" % command) + out_and_err_tuple = execute_with_stdin(command, stdin) + # Just log errors, since the output seems to include the output of the piped command too + if out_and_err_tuple.stdout: + logger.info(out_and_err_tuple.stdout.bytes) + if out_and_err_tuple.stderr: + logger.warn(out_and_err_tuple.stderr.bytes) + return out_and_err_tuple + + def run(self, commands, **kwargs): + predicate = kwargs.get('predicate', lambda: True) + validator = kwargs.get('validator', None) + stdin = kwargs.get('stdin', None) + pipe_commands = kwargs.get('pipe_commands', False) + if predicate(): + if validator: + validator() + if pipe_commands: + return self._exec_piped(commands, stdin) + else: + return self._exec(commands, stdin) + else: + if pipe_commands: + logger.info("Not Run: ") + logger.info(' | ' .join(map(lambda command: ' '.join(command), commands))) + else: + for command in commands: + logger.info("Not Run: %s", ' '.join(command)) + return None + +class Validator: + def __init__(self, value, predicate, exception): + self.value = value + self.predicate = predicate + self.exception = exception + + def __call__(self): + if_cond_raise(self.value, self.predicate, self.exception) + + +def formatted_timestamp(): + return datetime.datetime.now().strftime("%d%b%Y%H%M%S%f") + diff --git a/footprint/main/engines/__init__.py b/footprint/main/engines/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/engines/scenario_engine_setup.py b/footprint/main/engines/scenario_engine_setup.py new file mode 100644 index 000000000..fc89410b0 --- /dev/null +++ b/footprint/main/engines/scenario_engine_setup.py @@ -0,0 +1,169 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db.models.signals import post_syncdb, post_save, pre_delete +from django.dispatch import receiver +from south.signals import post_migrate +from django.conf import settings + +from footprint.main.policy.setup_scenario_policy import get_dev_acres +import models +from footprint.main.lib.functions import merge +from footprint import main +from footprint.main.models.config.scenario import Scenario +from footprint.main.models.keys.keys import Keys +from footprint.uf_tools import db_table_exists + + +__author__ = 'calthorpe_associates' + +@receiver(post_syncdb, sender=models) +@receiver(post_migrate, sender=models) +def on_post_syncdb(sender, **kwargs): + pass + +def on_scenario_post_save(sender, **kwargs): + check_basic_increments(kwargs['instance']) + +def on_scenario_pre_delete(sender, **kwargs): + pass + +def check_basic_increments(scenario): + from footprint.main.sql_unformatted import rawSQL + if not db_table_exists("basic_increments_{0}".format(scenario.id)): + from footprint.uf_tools import executeSQL_now + executeSQL_now(scenario.projects.inputs_outputs_db, [rawSQL.make_increment_headers.format(scenario.working_schema, scenario.id)]) + +# Register Django Signals to respond to synd_db and Scenario persistence +post_syncdb.connect(on_post_syncdb, sender=main.models) +post_save.connect(on_scenario_post_save, sender=Scenario) +pre_delete.connect(on_scenario_pre_delete, sender=Scenario) + +# TODO not sure what this was for +def get_project_options(project): + return {'base_year_grid' : project.resolve_db_entity(Keys.DB_ABSTRACT_BASE_FEATURE), + 'base_year' : project.base_year, + 'vmt_geography_type': 'taz', + 'vmt_geographies' : project.resolve_db_entity(Keys.DB_ENTITY_VMT_GEOGRAPHIES), + 'vmt_trip_table' : project.resolve_db_entity(Keys.DB_ENTITY_VMT_TRIPS), + 'ref_table_schema' : project.schema(), + 'srid' : project.srid, + 'study_area' : project.name, + 'key' : project.key, + 'working_schema' : project.schema() + } + +def get_engine_options(scenario): + + db = scenario.db + + conn_string = 'dbname=' + db['NAME'] + ' host=' + db['HOST'] + ' user=' + db['USER'] + ' password=' + db['PASSWORD'] + + scenario_settings = { + # scenario profile + 'use_hhsize_factor' : False, + 'pop_control' : None, + 'base_year_grid' : scenario.resolve_db_entity(Keys.DB_ABSTRACT_BASE_FEATURE), + 'developable_acres_grid' : scenario.resolve_db_entity(Keys.DB_ABSTRACT_DEVELOPABLE), + 'horiz_developable_grid' : scenario.resolve_db_entity(Keys.DB_ABSTRACT_HORIZON_DEVELOPABLE), + 'horiz_year_pt_grid' : scenario.resolve_db_entity(Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE), + 'canvas_schema' : settings.CANVAS_SCHEMA, + 'loaded_parcels' : scenario.resolve_db_entity(Keys.DB_ABSTRACT_LOADED_PARCELS), + 'parcel_portions' : scenario.resolve_db_entity(Keys.DB_ENTITY_PARCEL_PORTIONS), + 'working_schema' : scenario.schema(), + 'core_output' : scenario.resolve_db_entity(Keys.DB_CORE_OUTPUT), + 'scenario_name' : scenario.name, + 'scenario_year' : scenario.year, + 'scenario_region' : scenario.project.key, + 'placetypes' : scenario.resolve_db_entity(Keys.DB_PLACETYPES), + 'unique_name' : scenario.key(), + 'scenario_id' : str(scenario.id), + 'ref_table_schema' : scenario.schema(), + 'census_block_table' : scenario.resolve_db_entity(Keys.DB_ENTITY_CENSUS_BLOCKS), + 'census_blkgrp_table' : scenario.resolve_db_entity(Keys.DB_ENTITY_CENSUS_BLOCK_GROUPS) + } + + all_options = scenario_settings + + d = get_dev_acres(scenario) + devacres_settings = { + # developable acres configuration + 'Urban_Res_DetSF_SL' : d.urban_residential_detached_single_family_small_lot, + 'Urban_Res_DetSF_LL' : d.urban_residential_detached_single_family_large_lot, + 'Urban_Res_Mf' : d.urban_residential_multi_family, + 'Urban_Emp_Off' : d.urban_employment_office, + 'Urban_Emp_Ret' : d.urban_employment_retail, + 'Urban_Emp_Ind' : d.urban_employment_industrial, + 'Urban_Emp_Ag' : d.urban_employment_agriculture, + 'Urban_Emp_Mixed' : d.urban_employment_mixed, + 'Urban_Mixed_w_Off' : d.urban_mixed_use_with_office, + 'Urban_Mixed_no_Off' : d.urban_mixed_use_without_office, + 'Urban_No_Use' : d.urban_no_use, + 'gf' : d.greenfield, + # new fields added in v2 + 'Urban_Emp_Ret_Off' : d.greenfield, + 'Urban_Emp_Ind_Ret' : d.greenfield, + 'Urban_Emp_Ind_Off' : d.greenfield, + 'Urban_Mixed_Ag' : d.greenfield, + } + all_options = merge(all_options, devacres_settings) + + try: + from footprint.main.models.energy_water import EnergyWater + e = scenario.get_energy_water() + # e = EnergyWater.objectself.get(scenario=self) + YearsBasetoHoriz = scenario.year - 2010 + energywater_settings = { + #POLICY SETTINGS-------------------------------------------------------------------- + #Residential Energy (Electricity & Gas)-----Enter as PERCENTS!!!-------------------- + 'ResEnrgyNewConst' : e.ResEnrgyNewConst, #New construction - Percentage reduction from baseline rates by a horizon year. + 'ResEnrgyRetro' : e.ResEnrgyRetro, #Retrofits - Year-upon-year percentage reduction. + 'ResEnrgyReplcmt' : e.ResEnrgyReplcmt, #Replacement - Reset of some percentage of existing units to new standards. + #Commercial Energy (Electricity & Gas) --------------------------------------------- + 'ComEnrgyNewConst' : e.ComEnrgyNewConst, #New construction - Percentage reduction from baseline rates by a horizon year. + 'ComEnrgyRetro' : e.ComEnrgyRetro, #Retrofits - Year-upon-year - percentage reduction. + 'ComEnrgyReplcmt' : e.ComEnrgyReplcmt, #Replacement - Reset of some percentage of existing units to new standards. + #Residential Water (Indoor & Outdoor) ---------------------------------------------- + 'ResWatrNewConst' : e.ResWatrNewConst, #New construction + 'ResWatrRetro' : e.ResWatrRetro, #Retrofits + 'ResWatrReplcmt' : e.ResEnrgyReplcmt, #Replacement + #Commercial & Industrial Water (Indoor & Outdoor) ---------------------------------- + 'ComIndWatrNewConst' : e.ComIndWatrNewConst, #New construction + 'ComIndWatrRetro' : e.ComIndWatrRetro, #Retrofits + 'ComIndWatrReplcmt' : e.ComIndWatrReplcmt, #Replacement + #Additional Parameters ------------------------------------------------------------- + 'YearsBasetoHoriz' : YearsBasetoHoriz, + 'Water_GPCD_SF' : e.Water_GPCD_SF, #Indoor per-capita single family gallons per day + 'Water_GPCD_MF' : e.Water_GPCD_MF, #Indoor per-capita multifamily gallons per day: + 'Water_GPED_Retail' : e.Water_GPED_Retail, #Indoor per-employee gallons per day, Retail + 'Water_GPED_Office' : e.Water_GPED_Office, #Indoor per-employee gallons per day, Office + 'Water_GPED_Industrial' : e.Water_GPED_Industrial, #Indoor per-employee gallons per day, Industrial + 'Water_GPED_School' : e.Water_GPED_School, #Indoor per-employee gallons per day, School + #Industrial energy intensity: Annual Energy use per Employee + 'ann_ind_elec_peremp' : e.ann_ind_elec_peremp, #kwh + 'ann_ind_gas_peremp' : e.ann_ind_gas_peremp, #thm + } + all_options = merge(all_options, energywater_settings) + except Exception, E: + pass + + conn_string = {'conn_string' : conn_string,} + + all_options = merge(all_options, conn_string) + + return all_options + diff --git a/footprint/main/export_table_to_arc.py b/footprint/main/export_table_to_arc.py new file mode 100644 index 000000000..186adbf09 --- /dev/null +++ b/footprint/main/export_table_to_arc.py @@ -0,0 +1,114 @@ +#from calthorpe.footprint.models.database.information_schema import InformationSchema, GeometryColumns +from django.db import connections +from footprint.main.utils.multithreading import execute_sql +from models.database.information_schema import InformationSchema, GeometryColumns +import psycopg2 +from lib.functions import merge +import os + +os.environ['DJANGO_SETTINGS_MODULE'] = 'footprint.settings' + +options = dict( + export_table='grid150m_kern_loaded_census2010_placetyped_cr', + working_schema='sjv_scenarios', + conn_string='dbname=uf_restore host=10.0.0.133 user=calthorpe password=[PASSWORD]', +) + + +def executeSQL_now(conn_string, sqls, db=None, **kwargs): + """ + + :param conn_string: + :param sqls: + :param db: + :param kwargs: + :return: + """ + + cursor = connections['default'].cursor() + resultset = [] + + for sql in sqls: + try: + cursor.execute(sql) + result = cursor.fetchall() + + results = [] + for n in result: + results.append(n) + resultset.append(results) + + except Exception, E: + resultset.append(E) + + return resultset + + +def process_table(options): + print 'reprojecting table' + if 'srid' not in options: + options['srid'] = 4326 + + reproject = '''UPDATE {working_schema}.{export_table} + set wkb_geometry = ST_setSRID(ST_transform(wkb_geometry, {srid}),{srid});'''.format(**options) + + execute_sql(options['conn_string'], reproject) + + +def test(options, schema=None): + tables_with_geometry = InformationSchema.objects.tables_with_geometry(schema=schema) + + for information_scheme in tables_with_geometry: + table_dict = dict( + table_name = information_scheme.table_name, + table_schema = information_scheme.table_schema, + column_name = information_scheme.column_name + ) + sql = """select ST_CoordDim({column_name}), ST_SRID({column_name}), ST_GeometryType({column_name}) + from {schema_name}.{table_name}""".format(**table_dict) + ret = executeSQL_now(None, [sql])[0] + if len(ret)>0: + coord, srid, geom_type = ret[0] + else: + coord, srid, geom_type = (2, 4326, 'GEOMETRY') + GeometryColumns.objects.get_or_create( + **merge(dict( + f_table_name=information_scheme.table_name, + f_geometry_column=information_scheme.column_name, + f_table_schema=information_scheme.table_schema), + dict(defaults=dict( + # f_table_catalog=information_scheme.table_catalog, + coord_dimension=coord, + srid=srid, + type=geom_type + )))) + if False: + select_tables = "Truncate geometry_columns cascade; \n"\ + "select table_name, table_schema from information_schema.columns "\ + "where udt_name = 'geometry' "\ + "and table_schema = '{schema}';".format(schema=schema) + if not schema: + select_tables = """Truncate geometry_columns cascade; + select table_name, table_schema, column_name from information_schema.columns + where udt_name = 'geometry';""" + + tables_to_fix = executeSQL_now(options['conn_string'], [select_tables])[0] + updates = [] + for t in tables_to_fix: + table_dict = dict( + schema=t[1], + table_name=t[0], + geometry_column=t[2], + ) + print table_dict + update = ''' + INSERT INTO geometry_columns(f_table_catalog, f_table_schema, f_table_name, f_geometry_column, coord_dimension, srid, "type") + SELECT '', '{schema}', '{table_name}', '{geometry_column}', ST_CoordDim({geometry_column}), ST_SRID({geometry_column}), + ST_GeometryType({geometry_column}) + FROM {schema}.{table_name} LIMIT 1;'''.format(**table_dict) + updates.append(update) + executeSQL_now(options['conn_string'], updates) + + + +process_table(options) \ No newline at end of file diff --git a/footprint/main/initialization/__init__.py b/footprint/main/initialization/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/initialization/built_form/__init__.py b/footprint/main/initialization/built_form/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/initialization/built_form/built_form_derivatives.py b/footprint/main/initialization/built_form/built_form_derivatives.py new file mode 100644 index 000000000..e14fde2e4 --- /dev/null +++ b/footprint/main/initialization/built_form/built_form_derivatives.py @@ -0,0 +1,115 @@ +from footprint.main.lib.functions import map_to_dict +from footprint.main.models.built_form.building_use_definition import BuildingUseDefinition +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +class BuiltFormDerivatives(object): + def construct_primary_component_percents(self, placetype_components, primary_components, client): + """ + :return: BuildingPercent objects (UrbanFootprint v0.1 Built Form default set) + """ + primary_components = [] + import_primary_components = self.load_buildings_csv(client) + for import_primary_component in import_primary_components: + primary_component = dict( + primary_component_name=import_primary_component.name, + primary_component=import_primary_component, + placetype_component_name=import_primary_component.placetype_component, + placetype_component=placetype_components[import_primary_component.placetype_component], + percent=import_primary_component.percent_of_placetype_component + ) + primary_components.append(primary_component) + return primary_components + + def construct_building_use_percents(self, primary_components, client): + """ + :return: BuildingUsePercent objects (UrbanFootprint v0.1 Built Form default set) + """ + building_use_percents = [] + + def make_building_use_percent_dict(import_primary_component, building_use, building_use_category): + """ + uses the building_use_category to populate the sub-categories of building uses with + efficiency, square_feet_per_unit, and vacancy_rate + """ + if building_use == 'Armed Forces': + return None + import_use_field = building_use.lower().replace(' ', '_') + import_use_category_field = building_use_category.lower().replace(' ', '_') + use_percent = getattr(import_primary_component, 'percent_{0}'.format(import_use_field)) + if use_percent > 0: + + vacancy_rate = getattr(import_primary_component, 'vacancy_rate', 0) + efficiency = getattr(import_primary_component, '{0}_efficiency'.format(import_use_category_field), .85) + square_feet_per_unit = getattr(import_primary_component, + '{0}_square_feet_per_unit'.format(import_use_category_field), 10000) + + building_uses = dict( + vacancy_rate=vacancy_rate, + building_use_definition=BuildingUseDefinition.objects.get_or_create(name=building_use)[0], + percent=use_percent, + efficiency=efficiency, + square_feet_per_unit=square_feet_per_unit, + ) + + if import_primary_component.household_size > 0: + building_uses['household_size'] = import_primary_component.household_size + + building_use_percent = dict( + built_form_dict=primary_component, + built_form_name=primary_component['building_attributes']['name'], + built_form_uses=building_uses + ) + + return building_use_percent + + for import_primary_component in self.load_buildings_csv(client): + + primary_component = primary_components[import_primary_component.name] + for building_use, building_use_category in Keys.BUILDING_USE_DEFINITION_CATEGORIES.items(): + building_use_percent = make_building_use_percent_dict(import_primary_component, building_use, + building_use_category) + if building_use_percent: + building_use_percents.append(building_use_percent) + del building_use_percent + + return building_use_percents + + def construct_placetype_component_percents(self, placetype_dict, placetype_component_dict, client): + """ + :return: + """ + + import_placetype_components = [placetype_component for placetype_component in self.load_buildingtype_csv(client)] + + input_placetypes = self.load_placetype_csv(client) + input_placetype_dict = map_to_dict(lambda input_placetype: [input_placetype.name, input_placetype], + input_placetypes) + placetype_component_dict = dict() + + for name, placetype in placetype_dict.items(): + placetype_name = placetype['building_attributes']['name'].strip() + + for input_placetype_component in import_placetype_components: + + placetype_component_percent = getattr(input_placetype_component, + input_placetype_dict[placetype_name].clean_name) + + placetype_component_name = input_placetype_component.name.strip() + + default_placetype_component_dict = dict() + + placetype_component_dict[placetype_name] = placetype_component_dict.get(placetype_name, + default_placetype_component_dict) + + if placetype_component_percent > 0: + category = input_placetype_component.category.strip() + if category: + placetype_component_dict[placetype_name][placetype_component_name] = { + 'category': category, + 'percent': placetype_component_percent, + } + + return placetype_component_dict diff --git a/footprint/main/initialization/built_form/built_form_importer.py b/footprint/main/initialization/built_form/built_form_importer.py new file mode 100644 index 000000000..3f1e291be --- /dev/null +++ b/footprint/main/initialization/built_form/built_form_importer.py @@ -0,0 +1,280 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from collections import defaultdict +import csv +import importlib +import os +from footprint.main.initialization.built_form.built_form_derivatives import BuiltFormDerivatives +from footprint.main.initialization.built_form.import_primary_component import ImportPrimaryComponent +from footprint.main.initialization.built_form.import_placetype import ImportedPlacetype +from footprint.main.lib.functions import map_to_dict +from footprint.main.models.built_form.placetype import Placetype +from footprint.main.models.built_form.placetype_component_percent import PlacetypeComponentPercent +from footprint.main.models.built_form.building_use_percent import BuildingUsePercent +from footprint.main.models.built_form.primary_component_percent import PrimaryComponentPercent +from footprint.main.models.built_form.primary_component import PrimaryComponent +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from django.conf import settings + + +class BuiltFormImporter(BuiltFormDerivatives): + def __init__(self): + super(BuiltFormImporter, self).__init__() + self.dir = os.path.dirname(__file__) + + def built_form_path(self, client): + # TODO Let the client resolve this via a fixture + client_built_form_path = "{server_root}/footprint/client/configuration/{client}/built_form/import_csv".format( + server_root=settings.SERVER_ROOT, client=client) + + return client_built_form_path + + def load_buildings_csv(self, client): + """ + :param self.dir csv file self.directory + :return: ImportedBuilding objects imported from UrbanFootprint v0.1 Built Form default set, csv or a custom + set defined for the client + """ + # Load building attribute data from a csv and used it to create Building instances + path = '{0}/buildings.csv'.format(self.built_form_path(client)) + if not os.path.exists(path): + return [] + file = open(path, "rU") + imported_buildings = ImportPrimaryComponent.import_from_file(file) #import_from_filename(path) + return imported_buildings + + def load_buildingtype_csv(self, client): + """ + :return: ImportedBuildingtype objects imported from UrbanFootprint v0.1 Built Form default set, csv or a custom + set defined for the client + """ + if not os.path.exists(self.built_form_path(client)): + return [] + client_built_form = "footprint.client.configuration.{client}.built_form.{client}_import_placetype_component".format( + server_root=settings.SERVER_ROOT, client=client) + importer_module = importlib.import_module(client_built_form) + placetype_component_importer = importer_module.ImportPlacetypeComponent + imported_buildingtypes = placetype_component_importer.import_from_filename( + '{0}/buildingtypes.csv'.format(self.built_form_path(client))) + return imported_buildingtypes + + def load_placetype_csv(self, client): + """ + :return: ImportedPlacetype objects imported from UrbanFootprint v0.1 Built Form default set, csv or a custom + set defined for the client + """ + if not os.path.exists(self.built_form_path(client)): + return [] + imported_placetypes = ImportedPlacetype.import_from_filename( + '{0}/placetypes.csv'.format(self.built_form_path(client))) + return imported_placetypes + + def construct_primary_components(self, client='default'): + """ + :return: Dictionary keyed by Building name and valued by Building objects (UrbanFootprint v0.1 Built + Form default set) + """ + primary_components = [] + + for import_primary_component in self.load_buildings_csv(client): + building_attributes = dict( + name=import_primary_component.name, + floors=import_primary_component.floors, + total_far=import_primary_component.total_far, + softscape_and_landscape_percent=import_primary_component.softscape_and_landscape_percent, + pervious_hardscape_percent=import_primary_component.pervious_hardscape_percent, + impervious_hardscape_percent=import_primary_component.impervious_hardscape_percent, + impervious_roof_percent=import_primary_component.impervious_roof_percent, + parking_structure_square_feet=import_primary_component.parking_structure_square_feet, + parking_spaces=import_primary_component.parking_spaces, + residential_average_lot_size=import_primary_component.residential_average_lot_size, + irrigated_percent=import_primary_component.irrigated_percent, + ) + + primary_component = dict(building_attributes=building_attributes) + + primary_components.append(primary_component) + + return map_to_dict(lambda primary_component: + [primary_component['building_attributes']['name'], primary_component], + primary_components) + + def construct_placetype_components(self, client): + """ + :return: A dict keyed by BuildingType name and valued by BuildingType objects (UrbanFootprint v0.1 Built Form + default set) + """ + placetype_component_names = [] + placetype_components = [] + placetype_component_imports = self.load_buildingtype_csv(client) + for b in placetype_component_imports: + placetype_components.append(dict( + name=b.name, + color=b.color, + component_category=b.category + )) + + for placetype_component_name in set(placetype_component_names): + building_attributes = dict(name=placetype_component_name) + placetype_components.append(dict(building_attributes=building_attributes)) + + return map_to_dict(lambda placetype_component: + [placetype_component['name'], placetype_component], + placetype_components) + + def construct_placetypes(self, client): + """ + :return: PlaceType objects (UrbanFootprint v0.1 Built Form default set) + """ + + placetypes = [] + for placetype in self.load_placetype_csv(client): + building_attributes = dict(name=placetype.name) + placetype = dict( + building_attributes=building_attributes, + color=placetype.color, + intersection_density=placetype.intersection_density + ) + placetypes.append(placetype) + return map_to_dict( + lambda placetype: [placetype['building_attributes']['name'], placetype], placetypes) + + + def construct_built_forms(self, client): + """ + Calls all the functions necessary to generate the Built Forms to mimic the + starter set of v0.1 UrbanFootprint Built Forms + """ + if not os.path.exists(self.built_form_path(client)): + return {} + + built_form_dict = dict( + primary_component_lookup=self.construct_primary_components(client), + placetype_component_lookup=self.construct_placetype_components(client), + placetype_lookup=self.construct_placetypes(client) + ) + + primary_component_lookup = built_form_dict['primary_component_lookup'] + + return { + 'placetypes': built_form_dict['placetype_lookup'].values(), + 'placetype_components': built_form_dict['placetype_component_lookup'].values(), + 'primary_components': primary_component_lookup.values(), + 'primary_component_percents': self.construct_primary_component_percents( + built_form_dict['placetype_component_lookup'], + primary_component_lookup, + client=client), + 'building_use_percents': self.construct_building_use_percents(primary_component_lookup, client=client), + 'placetype_component_percents': self.construct_placetype_component_percents( + built_form_dict['placetype_lookup'], + built_form_dict['placetype_component_lookup'], client=client), + } + + + def construct_sample_built_forms(self, client): + """ + Builds a sample set of built forms for testing + """ + placetypes = self.construct_sample_placetypes() + buildingtypes = self.construct_sample_placetype_components(placetypes) + + building_percents = self.construct_sample_primary_component_percents(buildingtypes) + + buildings = [building['building'] for building in building_percents] + building_dict = map_to_dict(lambda building: [building['building_attributes']['name'], building], buildings) + buildingtype_dict = map_to_dict( + lambda buildingtype: [buildingtype['building_attributes']['name'], buildingtype], buildingtypes) + + return {'placetypes': placetypes, + 'placetype_components': buildingtypes, + 'primary_components': buildings, + 'primary_component_percents': building_percents, + 'building_use_percents': self.construct_building_use_percents(building_dict, client=client), + 'placetype_component_percents': self.construct_placetype_component_percents( + map_to_dict(lambda placetype: [placetype['building_attributes']['name'], placetype], placetypes), + buildingtype_dict, client=client)} + + + def construct_sample_placetypes(self): + """ + :return: a sample set of four placetypes + """ + pt_ids = [1, 2, 3, 8, 9, 10, 16, 20, 25, 29, 30] + sample_placetypes = list(self.construct_placetypes(client='default')[i] for i in pt_ids) + + return sample_placetypes + + + def construct_sample_placetype_components(self, sample_placetypes): + sample_placetype_components = [] + all_placetype_components = self.construct_placetype_components(client='default') + placetype_component_dict = map_to_dict( + lambda placetype_component: [placetype_component['building_attributes']['name'], placetype_component], + all_placetype_components + ) + + sample_buildingtype_percents = self.construct_placetype_component_percents( + map_to_dict(lambda placetype: [placetype['building_attributes']['name'], placetype], sample_placetypes), + placetype_component_dict) + + placetype_components = [] + for placetype, components in sample_buildingtype_percents.items(): + for placetype_components, attributes in components['placetype_components'].items(): + placetype_components.append(placetype_components) + + for placetype_components in set(placetype_components): + sample_placetype_components.append({'building_attributes': {'name': placetype_components}}) + + return sample_placetype_components + + def construct_sample_primary_component_percents(self, sample_placetype_components, client): + all_primary_components = self.construct_primary_components() + primary_component_dict = map_to_dict(lambda building: [building['building_attributes']['name'], building], + all_primary_components) + + sample_placetype_component_dict = map_to_dict( + lambda placetype_component: [placetype_component['building_attributes']['name'], placetype_component], + sample_placetype_components + ) + sample_primary_component_percents = [] + + for import_primary_component in self.load_buildings_csv(client): + component = import_primary_component.placetype_component + if component not in sample_placetype_component_dict: + print "BuildingType " + import_primary_component.placetype + " is not used in this set :: skipping" + continue + + placetype_component = sample_placetype_component_dict[component] + building_percent = dict( + primary_component_name=import_primary_component.name, + primary_component=primary_component_dict[import_primary_component.name], + placetype_component_name=import_primary_component.building_type, + placetype_component=placetype_component, + percent=import_primary_component.percent_of_building_type + ) + sample_primary_component_percents.append(building_percent) + + return sample_primary_component_percents + + + def delete_built_forms(self): + """ + Deletes all BuiltForm objects from the database. + """ + + Placetype.objects.all().delete() + PlacetypeComponentPercent.objects.all().delete() + BuildingUsePercent.objects.all().delete() + PrimaryComponentPercent.objects.all().delete() + PrimaryComponent.objects.all().delete() + PlacetypeComponent.objects.all().delete() + PlacetypeComponentPercent.objects.all().delete() diff --git a/footprint/main/initialization/built_form/import_placetype.py b/footprint/main/initialization/built_form/import_placetype.py new file mode 100644 index 000000000..8298be202 --- /dev/null +++ b/footprint/main/initialization/built_form/import_placetype.py @@ -0,0 +1,24 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from csvImporter.fields import CharField, FloatField +from csvImporter.model import CsvModel + +class ImportedPlacetype(CsvModel): + name = CharField(prepare=lambda x: x or '') + clean_name = CharField(prepare=lambda x: x or '') + color = CharField(prepare=lambda x: x or '') + intersection_density = FloatField(prepare=lambda x: x or 0) + + class Meta: + delimiter = "," + has_header = True diff --git a/footprint/main/initialization/built_form/import_placetype_component.py b/footprint/main/initialization/built_form/import_placetype_component.py new file mode 100644 index 000000000..09b41306b --- /dev/null +++ b/footprint/main/initialization/built_form/import_placetype_component.py @@ -0,0 +1,61 @@ +__author__ = 'calthorpe_associates' + +from csvImporter.fields import CharField, FloatField, IntegerField +from csvImporter.model import CsvModel + +__author__ = 'calthorpe_associates' + +class ImportPlacetypeComponent(CsvModel): +# BTID,Building_Type, +# Urban Mixed Use,Urban Residential,Urban Commercial,City Mixed Use,City Residential,City Commercial, +# Town Mixed Use,Town Residential,Town Commercial,Village Mixed Use,Village Residential,Village Commercial, +# Neighborhood Residential,Neighborhood Low,Office Focus,Mixed Office and R&D,Office/Industrial,Industrial Focus, +# Low-Density Employment Park,High Intensity Activity Center,Mid Intensity Activity Center, +# Low Intensity Retail-Centered N'Hood,Retail: Strip Mall/ Big Box,Industrial/Office/Res Mixed High, +# Industrial/Office/Res Mixed Low,Suburban Multifamily,Suburban Mixed Residential,Residential Subdivision, +# Large Lot Residential Area,Rural Residential,Rural Ranchettes,Rural Employment,Campus/ University, +# Institutional,Parks & Open Space,BuildingType Name,Gross_Net_Flag + category = CharField(prepare=lambda x: x or '') + btid = IntegerField(prepare=lambda x: x or 0) + color = CharField(prepare=lambda x: x or '') + name = CharField(prepare=lambda x: x or '') + urban_mixed_use = FloatField(prepare=lambda x: x or 0) + urban_residential = FloatField(prepare=lambda x: x or 0) + urban_commercial = FloatField(prepare=lambda x: x or 0) + city_mixed_use = FloatField(prepare=lambda x: x or 0) + city_residential = FloatField(prepare=lambda x: x or 0) + city_commercial = FloatField(prepare=lambda x: x or 0) + town_mixed_use = FloatField(prepare=lambda x: x or 0) + town_residential = FloatField(prepare=lambda x: x or 0) + town_commercial = FloatField(prepare=lambda x: x or 0) + village_mixed_use = FloatField(prepare=lambda x: x or 0) + village_residential = FloatField(prepare=lambda x: x or 0) + village_commercial = FloatField(prepare=lambda x: x or 0) + neighborhood_residential = FloatField(prepare=lambda x: x or 0) + neighborhood_low = FloatField(prepare=lambda x: x or 0) + office_focus = FloatField(prepare=lambda x: x or 0) + mixed_office_and_r_and_d = FloatField(prepare=lambda x: x or 0) + office_industrial = FloatField(prepare=lambda x: x or 0) + industrial_focus = FloatField(prepare=lambda x: x or 0) + low_density_employment_park = FloatField(prepare=lambda x: x or 0) + high_intensity_activity_center = FloatField(prepare=lambda x: x or 0) + mid_intensity_activity_center = FloatField(prepare=lambda x: x or 0) + low_intensity_retail_centered_neighborhood = FloatField(prepare=lambda x: x or 0) + retail_strip_mall_big_box = FloatField(prepare=lambda x: x or 0) + industrial_office_residential_mixed_high = FloatField(prepare=lambda x: x or 0) + industrial_office_residential_mixed_low = FloatField(prepare=lambda x: x or 0) + suburban_multifamily = FloatField(prepare=lambda x: x or 0) + suburban_mixed_residential = FloatField(prepare=lambda x: x or 0) + residential_subdivision = FloatField(prepare=lambda x: x or 0) + large_lot_residential = FloatField(prepare=lambda x: x or 0) + rural_residential = FloatField(prepare=lambda x: x or 0) + rural_ranchettes = FloatField(prepare=lambda x: x or 0) + rural_employment = FloatField(prepare=lambda x: x or 0) + campus_or_university = FloatField(prepare=lambda x: x or 0) + institutional = FloatField(prepare=lambda x: x or 0) + parks_and_open_space = FloatField(prepare=lambda x: x or 0) + + class Meta: + delimiter = "," + has_header = True + diff --git a/footprint/main/initialization/built_form/import_primary_component.py b/footprint/main/initialization/built_form/import_primary_component.py new file mode 100644 index 000000000..ae1d413d5 --- /dev/null +++ b/footprint/main/initialization/built_form/import_primary_component.py @@ -0,0 +1,117 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from csvImporter.fields import CharField, FloatField, IntegerField +from csvImporter.model import CsvModel + +__author__ = 'calthorpe_associates' + +class ImportPrimaryComponent(CsvModel): + # id + id = IntegerField(prepare=lambda x: x or 0) + #source,hyperlink,building_type, + source = CharField(prepare=lambda x: x or '') + hyperlink = CharField(prepare=lambda x: x or '') + placetype_component = CharField(prepare=lambda x: x or '') + #building,household_size, + name = CharField(prepare=lambda x: x or '') + + vacancy_rate = FloatField(prepare=lambda x: x or 0) + household_size = FloatField(prepare=lambda x: x or 0) + + #all_uses, + all_uses = CharField(prepare=lambda x: x or '') + + du_density = CharField(prepare=lambda x: x or '') + emp_density = CharField(prepare=lambda x: x or '') + +#Pct_SF_Large_Lot,Pct_SF_Small_Lot,Pct_Attached_SF,Pct_MF_2_to_4,Pct_MF_5_Plus, + percent_single_family_large_lot = FloatField(prepare=lambda x: x or 0) + percent_single_family_small_lot = FloatField(prepare=lambda x: x or 0) + percent_attached_single_family = FloatField(prepare=lambda x: x or 0) + percent_multifamily_2_to_4 = FloatField(prepare=lambda x: x or 0) + percent_multifamily_5_plus = FloatField(prepare=lambda x: x or 0) + +# Pct_Emp_Office_Svc,Pct_Educ_Svc,Pct_Medical_Svc,Pct_Public_Admin, + percent_office_services = FloatField(prepare=lambda x: x or 0) + percent_education_services = FloatField(prepare=lambda x: x or 0) + percent_medical_services = FloatField(prepare=lambda x: x or 0) + percent_public_admin = FloatField(prepare=lambda x: x or 0) + +#Pct_Retail_Svc,Pct_Restuarant,Pct_Accommodation,Pct_Arts_Entertainment,Pct_Other_Svc, + percent_retail_services = FloatField(prepare=lambda x: x or 0) + percent_restaurant = FloatField(prepare=lambda x: x or 0) + percent_accommodation = FloatField(prepare=lambda x: x or 0) + percent_arts_entertainment = FloatField(prepare=lambda x: x or 0) + percent_other_services = FloatField(prepare=lambda x: x or 0) + +# Pct_Manufacturing,Pct_Transport_warehouse,Pct_Wholesale,Pct_Construction_Util,Pct_Agriculture,Pct_Extraction, + percent_manufacturing = FloatField(prepare=lambda x: x or 0) + percent_transport_warehouse = FloatField(prepare=lambda x: x or 0) + percent_wholesale = FloatField(prepare=lambda x: x or 0) + percent_construction_utilities = FloatField(prepare=lambda x: x or 0) + + percent_agriculture = FloatField(prepare=lambda x: x or 0) + percent_extraction = FloatField(prepare=lambda x: x or 0) + +# percent_of_building_type,floors,percent_residential,percent_retail,percent_office,percent_industrial, + percent_of_placetype_component = FloatField(prepare=lambda x: x or 0) + floors = FloatField(prepare=lambda x: x or 0) + + # percent_residential = FloatField(prepare=lambda x: x or 0) + # percent_retail = FloatField(prepare=lambda x: x or 0) + # percent_office = FloatField(prepare=lambda x: x or 0) + # percent_industrial = FloatField(prepare=lambda x: x or 0) + +#total_far,parking_spaces,parking_structure_square_feet,residential_efficiency,residential_lot_square_feet,square_feet_per_du, + total_far = FloatField(prepare=lambda x: x or 0) + parking_spaces = FloatField(prepare=lambda x: x or 0) + parking_structure_square_feet = FloatField(prepare=lambda x: x or 0) + + residential_efficiency = FloatField(prepare=lambda x: x or 0) + residential_average_lot_size = FloatField(prepare=lambda x: x or 0) + residential_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + +# retail_efficiency,retail_square_feet_per_employee,office_efficiency,office_square_feet_per_employee, + retail_efficiency = FloatField(prepare=lambda x: x or 0) + retail_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + office_efficiency = FloatField(prepare=lambda x: x or 0) + office_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + +#industrial_efficiency,industrial_square_feet_per_employee, + industrial_efficiency = FloatField(prepare=lambda x: x or 0) + industrial_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + +# misc_percent_of_retail_use,retail_percent_of_retail_use,restaurant_percent_of_retail_use,grocery_percent_of_retail_use, + misc_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + retail_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + restaurant_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + grocery_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + +# hardscape_percent,irrigated_percent,impervious_roof_percent,impervious_hardscape_percent,pervious_hardscape_percent,softscape_landscape_percent, + hardscape_percent = FloatField(prepare=lambda x: x or 0) + irrigated_percent = FloatField(prepare=lambda x: x or 0) + impervious_roof_percent = FloatField(prepare=lambda x: x or 0) + impervious_hardscape_percent = FloatField(prepare=lambda x: x or 0) + pervious_hardscape_percent = FloatField(prepare=lambda x: x or 0) + softscape_and_landscape_percent = FloatField(prepare=lambda x: x or 0) + + id2 = IntegerField(prepare=lambda x: x or 0) + + class Meta: + delimiter = "," + has_header = True diff --git a/footprint/main/initialization/data_provider.py b/footprint/main/initialization/data_provider.py new file mode 100644 index 000000000..f656fcf58 --- /dev/null +++ b/footprint/main/initialization/data_provider.py @@ -0,0 +1,228 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.contrib.auth.models import User, Permission +from nose.config import user_config_files +from tastypie.models import ApiKey +from footprint.client.configuration.fixture import BuiltFormFixture, ConfigEntitiesFixture, PolicyConfigurationFixture, InitFixture +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.lib.functions import remove_keys, get_single_value_or_create, merge, flat_map +from footprint.main.models import Medium +from footprint.main.models.category import Category +from footprint.main.models.config.project import Project +from footprint.main.models.config.region import Region +from footprint.main.models.config.global_config import global_config_singleton + +from django.conf import settings + +__author__ = 'calthorpe_associates' + +config_entities_fixture = resolve_fixture("config_entity", "config_entities", ConfigEntitiesFixture, settings.CLIENT) +policy_fixture = resolve_fixture("policy", "policy", PolicyConfigurationFixture, settings.CLIENT) +init_fixture = resolve_fixture(None, "init", InitFixture, settings.CLIENT) + +import logging + +logger = logging.getLogger(__name__) + + +class SQLImportError(Exception): + def __init__(self, value): + super(SQLImportError, self).__init__(value) + + +class DataProvider(object): + """ + This class loads test data from sources in the test_data directory. + All persistence is performed here, and instances of persisted classes are returned to the + caller. + """ + + def users(self): + for user_configuration in init_fixture.users: + self.user(**user_configuration) + + def user(self, username='test', password='test', email='testy@test.ca', api_key='TEST_API_KEY'): + """ + Create a user. + :return: + """ + username = 'test' + password = 'test' + email = 'testy@test.ta' + + def create(): + user = User.objects.create_user(username, email, password) + # Make sure the user has permission to update everything for testing purposes + user.user_permissions.add(*list(Permission.objects.all())) + return user + # An api key is created upon creating a user + + user = get_single_value_or_create(User.objects.filter(username=username), create) + api_key_instance = ApiKey.objects.get_or_create(user=user)[0] + if api_key_instance.key != api_key: + api_key_instance.key = api_key + api_key_instance.save() + return {'user': user, 'api_key': api_key_instance} + + + def scenarios(self, scenario_fixtures=None, project_fixtures=config_entities_fixture.projects(), **kwargs): + """ + Initializes scenarios using fixture data. The fixture data is expected in the form + dict(BaseScenario=[dict(),...], FutureScenario=[dict()....]) where the dicts in the former are used + to create BaseScenario instances and those in the latter to create FutureScenario instances. + Use kwargs to limit class processing to one model class with e.g. class=FutureScenario + :param scenario_fixtures: + :return: + """ + projects = self.projects(project_fixtures, **kwargs) + + # Get the scenario fixtures for each Project instance and build the Scenario instances. + # Flatten the results and return them + # scenario_fixtures may be a function that accepts the current project in order to filter the fixtures + return flat_map( + lambda project: self.scenarios_per_project( + project, + scenario_fixtures or + # Resolve as the scenarios as specific to the project scope as available + resolve_fixture("config_entity", + "config_entities", + ConfigEntitiesFixture, + project.schema()).scenarios(project), **kwargs), + projects + ) + + def scenarios_per_project(self, project, scenario_fixtures, **kwargs): + # Create the Scenarios from the fixtures + # The fixtures are dict keyed by the Scenario subclass (BaseScenario and FutureScenario) with a list of + # Scenario fixtures for each + scenarios_created_updated = map( + lambda scenario_fixture: + scenario_fixture['class_scope'].objects.update_or_create( + key=scenario_fixture['key'], + defaults=merge(remove_keys(scenario_fixture, + ['class_scope', 'key', 'project_key', 'categories', 'selections', 'year']), { + 'parent_config_entity': project, + 'year': scenario_fixture.get('year', project.base_year) + })), + # If kwargs['limit_to_classes'] is specified, only do Scenario subclasses that match it, if any + filter(lambda scenario_fixture: + scenario_fixture['class_scope'] in kwargs.get('limit_to_classes', [scenario_fixture['class_scope']]) + or [scenario_fixture['class_scope']], + scenario_fixtures)) + + for scenario_tuple in scenarios_created_updated: + logger.info("{update_or_create} Scenario {config_entity}".format(update_or_create='Created' if scenario_tuple[1] else 'Updated', config_entity=scenario_tuple[0])) + + # Apply the categories, and other simple many-to-many attributes as needed + for i, scenario_dict in enumerate(scenario_fixtures): + for category in scenario_dict.get('categories', []): + category, created, updated = Category.objects.update_or_create(key=category.key, value=category.value) + scenario = scenarios_created_updated[i][0] + scenario.add_categories(category) + scenario.save() + + return map(lambda scenario_created_updated: scenario_created_updated[0], scenarios_created_updated) + + def projects(self, project_fixtures=config_entities_fixture.projects(), region_fixtures=config_entities_fixture.regions(), **kwargs): + """ + Create test projects according to the samples + :param project_fixtures: + :return: + """ + + def update_or_create_project(project_dict): + project_tuple = Project.objects.update_or_create( + key=project_dict['key'], + defaults=merge(remove_keys(project_dict, ['key', 'base_table', 'region_index', 'media']), { + 'parent_config_entity': self.regions(region_fixtures, **kwargs)[project_dict['region_index']] + })) if Project in kwargs.get('limit_to_classes', [Project]) or [Project] else (Project.objects.get(key=project_dict['key']), False, False) + logger.info("{update_or_create} Project {config_entity}".format(update_or_create='Created' if project_tuple[1] else 'Updated', config_entity=project_tuple[0])) + + media = map(lambda medium_config: + Medium.objects.update_or_create( + key=medium_config.key, + defaults=remove_keys(medium_config.__dict__['kwargs'], 'key'))[0], + project_dict.get('media', [])) + + existing_media = project_tuple[0].media.filter(id__in=map(lambda medium: medium.id, media)) + media_to_add = set(media) - set(existing_media) + if len(media_to_add) > 0: + project_tuple[0].media.add(*media_to_add) + return project_tuple + + projects_created_updated = map( + lambda project_dict: update_or_create_project(project_dict), + project_fixtures) + + for project, created, updated in projects_created_updated: + + if created: + # Fire signals + project.save() + return map(lambda project_created_updated: project_created_updated[0], projects_created_updated) + + def regions(self, region_fixtures=config_entities_fixture.regions(), **kwargs): + """ + Create test regions according to the sample + :return: + """ + + def update_or_create_region(region_dict): + region_tuple = Region.objects.update_or_create( + key=region_dict['key'], + defaults=merge(remove_keys(region_dict, ['key', 'media']), { + 'parent_config_entity': global_config_singleton() + })) if Region in kwargs.get('limit_to_classes', [Region]) or [Region] else (Region.objects.get(key=region_dict['key']), False, False) + + logger.info("{update_or_create} Region {config_entity}".format(update_or_create='Created' if region_tuple[1] else 'Updated', config_entity=region_tuple[0])) + + media = map(lambda medium_config: + Medium.objects.update_or_create( + key=medium_config.key, + defaults=remove_keys(medium_config.__dict__['kwargs'], 'key'))[0], + region_dict.get('media', [])) + + existing_media = region_tuple[0].media.filter(id__in=map(lambda medium: medium.id, media)) + media_to_add = set(media) - set(existing_media) + if len(media_to_add) > 0: + region_tuple[0].media.add(*media_to_add) + return region_tuple + + regions_tuple = map( + lambda region_dict: update_or_create_region(region_dict), + region_fixtures) + + for region, created, updated in regions_tuple: + if created: + # Fire signals + region.save() + + return map(lambda region_tuple: region_tuple[0], regions_tuple) + + def import_table_name(self, db_entity): + """ + Returns the full db_entity table name (schema+table) and optionally adds the suffix + _sample if a sample-size table shall be used for importing + :param db_entity: + :return: + """ + full_table_name = db_entity.full_table_name + return "{0}_{1}".format(full_table_name, 'sample') if settings.USE_SAMPLE_DATA_SETS else full_table_name + + diff --git a/footprint/main/lib/__init__.py b/footprint/main/lib/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/lib/diagnostics.py b/footprint/main/lib/diagnostics.py new file mode 100644 index 000000000..02a229f8b --- /dev/null +++ b/footprint/main/lib/diagnostics.py @@ -0,0 +1,24 @@ +from Queue import Queue + +__author__ = 'calthorpe_associates' + +from footprint.main.urls import urlpatterns + +queue = Queue() +def show_urls(urllist=urlpatterns, depth=0): + """ + Inspects the URL regexes + :param urllist: + :param depth: + :return: + """ + if (depth > 50): + return + for entry in urllist: + queue.put(entry) + while not queue.empty(): + entry = queue.get() + print "{0} - {1}".format(" " * depth, entry.regex.pattern) + if hasattr(entry, 'url_patterns'): + show_urls(entry.url_patterns, depth + 1) + diff --git a/footprint/main/lib/functions.py b/footprint/main/lib/functions.py new file mode 100644 index 000000000..2a19e5695 --- /dev/null +++ b/footprint/main/lib/functions.py @@ -0,0 +1,632 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +# This module stores common functional-style functions that are not class specific. +# They should operate on any type and have qualities such as chainable or passable as arguments to other functions +# Make sure you check out Python's functools and itertools to see if what you need already exists before you write it. +from inspect import isfunction +from itertools import chain + +__author__ = 'calthorpe_associates' + +import collections + + +def to_iterable(object, + if_iterable=lambda object: object, + if_uniterable=lambda object: [object], + is_iterable=lambda object: is_iterable(object)): + """ + Returns an array containing the argument if it is not already iterable. + + Keyword arguments: + is_iterable -- You may optionally specify this lambda if the default is an insufficient test. (default isinstance(object, Collections.Iterable)) For example, if testing an object like a string that is already iterable, you may want to make a special is_iterable lambda that checks to see if the object is a string in an array or not + if_iterable -- An optional lambda to specify a mapping of the object if already iterable (default returns object) + if_uniterable -- An optional lambda to specify a mapping of the object if not iterable (default returns [object]) + + """ + return if_iterable(object) if is_iterable(object) else if_uniterable(object) + + +def is_list_or_tuple(object): + return isinstance(object, (list, tuple)) + + +def is_list_tuple_or_dict(object): + return isinstance(object, (list, tuple, dict)) + +def to_list(object, + if_list_or_tuple=lambda object: object, + if_not_list_nor_tuple=lambda object: [object], + is_list_or_tuple=is_list_or_tuple +): + """ + Returns an array containing the argument if it is not already an array + + Keyword arguments: + is_list_or_tuple -- You may optionally specify this lambda if the default is an insufficient test. (default isinstance(object, Array)) + if_list_or_tuple -- An optional lambda to specify a mapping of the object if already a list or tuple (default returns object) + if_not_list_nor_tuple -- An optional lambda to specify a mapping of the object if not a list or tuple (default returns [object]) + + """ + return if_list_or_tuple(object) if is_list_or_tuple(object) else if_not_list_nor_tuple(object) + + +def to_single_if_one_item(list): + """ + The opposite of to_list. Returns a single item if the give list has exactly on item. Otherwise returns a list + :param list: + :return: + """ + return list[0] if len(list) == 1 else list + + +def map_item_or_items_or_dict_values(item_lambda, data): + """ + Processes each item or the non-list single item with item_lambda and returns a list or single item accordingly + :param item_lambda: + :param data + :return: + """ + if (isinstance(data, dict)): + # Map to a new dict. Pass through the keys and map the values + return map_dict_to_dict(lambda key, value: [key, item_lambda(value)], data) + else: + # Map the item or list, return a single item or array + return to_single_if_one_item(map(item_lambda, to_list(data))) + + +def is_iterable(object): + return isinstance(object, collections.Iterable) + + +def to_dict_if_none(object): + """ + Forces an object to be an empty dictionary if it is null + """ + return {} if not object else object + + +def merge(*args): + """ + Merges any number dictionaries together. The last duplicate dictionary keys always take precedence + + Keyword arguments: + **args: any number of dictionaries + """ + super_dictionary = {} + for dictionary in args: + super_dictionary = dict(super_dictionary.items() + dictionary.items()) + return super_dictionary + +def deep_merge(*args): + """ + Like merge but combines values that are both dicts by deep_merging them + :param args: + :return: + """ + super_dictionary = {} + for dictionary in args: + + matching_keys_of_dicts = filter( + lambda key: type(super_dictionary[key]) == dict and type(dictionary[key] == dict), + set(super_dictionary.keys()).intersection(set(dictionary.keys()))) + merged_dict = map_to_dict(lambda key: [key, deep_merge(super_dictionary[key], dictionary[key])], matching_keys_of_dicts) + + non_matching_keys = set(super_dictionary.keys()).union(set(dictionary.keys())).difference(matching_keys_of_dicts) + super_dictionary = dict( + filter(lambda tup: tup[0] in non_matching_keys, super_dictionary.items() + dictionary.items()) + + merged_dict.items()) + return super_dictionary + + +def ordered_dict_merge(*args): + """ + Merges any number dictionaries together. The last duplicate dictionary keys always take precedence + + Keyword arguments: + **args: any number of dictionaries + """ + super_dictionary = collections.OrderedDict() + for dictionary in args: + super_dictionary = collections.OrderedDict(super_dictionary.items() + dictionary.items()) + return super_dictionary + + +def filter_keys(dct, keys): + """ + Remove all keys not given in keys from the dict, returning a copy with only the specified keys + :param dict: + :param keys: + :return: + """ + return dict((k, v) for k, v in dct.iteritems() + if k in keys) + + +def remove_keys(dct, keys): + """ + Remove the given keys from the dict, returning a copy of the dict with the keys removed + :param dct: + :param keys: Keys to match. If concatinated strings are used then recurse into the dictionaries to remove + those. For instance 'foo.bar' removes bar keys in foo keyed dictionaries 'foo.*.bar' removes bar keys in + any key under a foo key + :return: + """ + return dict((k, + # Recurs on v if it's a dict, only passing through matching keys + remove_keys(v, parse_key_strings(keys, k)) if isinstance(v, dict) else v) + for k, v in dct.iteritems() if not k in keys) + +def parse_key_strings(keys, segment): + keys_segments = map(lambda key: key.split('.'), keys) + return map(lambda key_segments: '.'.join(key_segments[1:]), + filter(lambda key_segments: len(key_segments) > 1 and (key_segments[0] in ['*', segment]), keys_segments)) + +def remove_items(array, keys): + """ + Remove the given keys from the dict, returning a copy of the dict with the keys removed + :param array: + :param keys: + :return: + """ + return filter(lambda item: item not in keys, array) + + +def dual_map(lambda_call, list1, list2): + if len(list1) > len(list2): + raise Exception('list1 has more elements than list2: {0} versus {1}'.format(list1, list2)) + return map(lambda index_and_val: lambda_call(index_and_val[1], list2[index_and_val[0]]), + enumerate(list1)) + + +def dual_map_to_dict(lambda_call, list1, list2): + """ + Dual map two even length enumerables to key/value pairs + :param lambda_call: Takes the first and second arguments as args and allows transformation of each, returning + a two elemetn array to be the key and value + :param list1: + :param list2: + :return: A dictionary + """ + if len(list1) > len(list2): + raise Exception('list1 has more elements than list2: {0} versus {1}'.format(list1, list2)) + return map_to_dict( + lambda index_and_val: lambda_call(index_and_val[1], list2[index_and_val[0]]), + enumerate(list1)) + + +def map_dict(lambda_call, dict): + """ + Maps the dict to key values. + + lambda_call takes each item and must returns a key and value as a tuple or list + """ + return map(lambda tup: lambda_call(tup[0], tup[1]), dict.iteritems()) + +def compact_dict(dictionary): + """ + Removes any key values of the dict that are null, returning a new dict + :param dictionary + :return: + """ + results = {} + for key, value in dictionary.iteritems(): + if value: + results[key] = value + return results + +def map_to_dict(lambda_call, list): + """ + Maps the list to key values. + + lambda_call takes each item and must returns a key and value as a tuple or list + """ + results = {} + for item in list: + result_pair = lambda_call(item) + results[result_pair[0]] = result_pair[1] + return results + + +def map_dict_to_dict(lambda_call, dictionary): + """ + Maps the list to key values. + + lambda_call takes each a key and value and must returns a key and value as a tuple or list + """ + results = {} + for key, value in dictionary.iteritems(): + result_pair = lambda_call(key, value) + if result_pair: + results[result_pair[0]] = result_pair[1] + return results + +def map_dict_value(lambda_call, dictionary): + """ + Like map_dict_to_dict, but leaves the key alone and only maps the values + :param lambda_call: A function expecting a value. If you need the key use map_dict_to_dict. Returns the mapped value + :param dictionary: + :return: A dictionary with the keys unchanged but values mapped + """ + return map_dict_to_dict(lambda key, value: [key, lambda_call(value)], dictionary) + +def map_to_keyed_collections(lambda_call, list): + """ + Creates a dict keyed by the results of the lambda_call for each item and valued by the items themselves. items with the same key results are placed together in a selection + :param lambda_call: Takes an item and returns the key to which it should map. Optionally return an object as the second return value. This will be used in place of the item for the dict value + :param dictionary: + :return: + """ + result = {} + for item in list: + val = lambda_call(item) + if is_list_or_tuple(val): + key, obj = val + else: + key = val + obj = item + if not result.get(key, None): + result[key] = [] + result[key].append(obj) + return result + + +def flatten_values(dct): + """ + Flattens the values of a dict containing arrays for values (the result of map_to_keyed_collections) + :param dct: e.g. {'a':[1,2,3],'b':[3,4,5]} + :return: [1,2,3,3,4,5] + """ + return flatten([v for k, v in dct.iteritems()]) + + +def flat_map_values(lambda_call, dct): + """ + Like flatten_values but also accepts a lambda which accepts each key and value and returns a collection result to be flattened. This is useful to make values of a particular key unique + :param lambda_call: + :param dct: + :return: + """ + return flatten([lambda_call(k, v) for k, v in dct.iteritems()]) + + +def filter_dict(lambda_call, dictionary): + """ + Filters a dictionary + :param lambda_call: takes each kay and value and returns true to pass the set through the filter + :param dictionary: + :return: + """ + return dict((key, value) for key, value in dictionary.iteritems() if lambda_call(key, value)) + + +def get_value_of_first_matching_key(filter_lambda, dict, default): + """ + Return the value of the first key that makes the filter_lambda return true, or else return the default. Since key order is arbitrary, you should only use this if one or no keys are expected to pass + :param filter_lambda: + :param dict: + :param default: + :return: + """ + filtered_dict = filter_dict(filter_lambda, dict) + return filtered_dict[filtered_dict.keys()[0]] if len(filtered_dict.keys()) > 0 else default + + +def map_to_2d_dict(outer_lambda, inner_lambda, list): + """ + Create a 2D dictionary from the list + outer_lambda should return a single item, the outer hash key + inner_lambda should return a pair, the inner hash key and the value + """ + results = {} + for item in list: + outer_key = outer_lambda(item) + result_pair = inner_lambda(item) + if not results.get(outer_key, False): + results[outer_key] = {} + results[outer_key].update({result_pair[0]: result_pair[1]}) + + return results + + +def if_cond(value, predicate_lambda, map_lambda): + """ + A simple wrapper to test a value with a predicate lambda and call a map lambda if the predicate is true + value is the object to test + predicate_lambda takes value and calls map_lambda if true or else returns value + Returns value of the predicate returns false. Returns the map lambda result if the predicate returns true + """ + if (predicate_lambda(value)): + return map_lambda(value) + return value + + +def if_cond_raise(value, predicate_lambda, exception_lambda): + """ + Raises the exception returned by exception_lambda if predicate_lambda returns true. Both lambdas are passed value. + """ + if (predicate_lambda(value)): + raise exception_lambda(value) + return value + + +def if_false_raise(predicate, exception_lambda): + """ + Raises the exception returned by the exception_lambda if predicate is true. + """ + if predicate: + raise exception_lambda() + + +def first(f, seq): + """Return first item in sequence where f(item) == True.""" + for item in seq: + if f(item): + return item + + +def flatten(list_of_lists): + """ + Shallow flatten a multi-dimensional list, meaning the top two dimensions are flattened and deeper ones are ignored + """ + return list(chain.from_iterable(list_of_lists)) + + +def flat_map(map_lambda, list): + """ + Map a list to a list of lists and flatten the results + """ + return flatten(map(map_lambda, list)) + +def flat_map_dict(map_dict_lambda, dct): + """ + Maps a dict to a list per dict entry, then flattens those lists + :param map_dict_lambda: + :param dct: + :return: + """ + return flatten(map_dict(map_dict_lambda, dct)) + + +def unique(seq, idfun=None): +# order preserving + if idfun is None: + def idfun(x): return x + seen = {} + result = [] + for item in seq: + marker = idfun(item) + if (not marker in seen): + seen[marker] = 1 + result.append(item) + return result + + +def any_true(predicate_lambda, list): + for item in list: + if predicate_lambda(item): + return True + return False + + +def get_single_value_or_none(list): + return list[0] if len(list) == 1 else None + +def get_single_value_or_create(list, construct): + """ + Returns the single value of the list if it has exactly one member or else calls the construct lambda + :param list: + :param construct: + :return: + """ + return list[0] if len(list) == 1 else construct() + + +def get_list_or_if_empty(list, default_lambda): + """ + Returns teh given list if not-empty or calls the default_lambda and returns the results of it + :param list: + :param default_lambda: + :return: + """ + return list if len(list) > 0 else default_lambda() + + +class TooManyFoundException(Exception): + pass + + +def one_or_none(list): + """ + Raise an exception unless 0 or 1 results are in list + :return: the single item or None + """ + if len(list) > 1: + raise TooManyFoundException("Expected 0 or 1 result but found {0}. Found: {1}".format(len(list), list)) + return list[0] if len(list) == 1 else None + + +def deep_copy_dict_structure(dct, convert_objects=False): + """ + Traverse a dict, recreating the internal dict instances but leaving the non-dict values as identical references + :param dct: The dict to deep copy + :param convert_objects: Default False. Object instances are normally copied without altercation. If this is set to True then the __dict__ property will be used for the copy. This is removes the evidence of the class but is useful for converting to a simple dict/list/primitive structure + :return: + """ + return map_dict_to_dict(lambda key, value: [key, deep_copy(value, convert_objects)], dct) + + +def deep_copy_array_structure(array, convert_objects=False): + return map(lambda value: deep_copy(value, convert_objects), array) + + +def deep_copy(value, convert_objects=False): + if isinstance(value, dict): + return deep_copy_dict_structure(value, convert_objects) + elif is_list_or_tuple(value): + return deep_copy_array_structure(value, convert_objects) + elif convert_objects and hasattr(value, '__dict__'): + # Attempt to handle objects + return deep_copy_dict_structure(value.__dict__, convert_objects) + else: + # Primitives (or objects when convert_object==False) + return value + + +def type_wrap(value, wrapper_lambdas): + """ + Wraps the value using one of the wrapper_lambdas or returns value if no match is found + :param value: + :param wrapper_lambdas: dict of lambda wrappers, keyed by the type they should wrap. These wrappers can wrap all dict values and the outer dict. They can match the dict class itself or any other class. The wrapper will be looked up using wrapper_lambdas.get([type(value)], get_first(filter(lambda wrapper_key: isinstance(value, wrapper_key), wrapper_lambdas.keys()), deep_map_dict_structure(value)) so that if an exact match isn't found the keys will be searched for a parent class. If no match is found the value is returned as is + :param the optional container of the value, for use by the lambda, which takes it as an optional second value + :return: + """ + # Try a direct key type match + x = wrapper_lambdas.get(type(value), + # else lookup by inheritance. + get_value_of_first_matching_key( + lambda wrapper_key, wrapper_value: isinstance(value, wrapper_key), + wrapper_lambdas, + # else return value + lambda value: value))(value) + return x + + +def deep_map_dict_structure(dcts_or_dct, wrapper_lambdas): + """ + Deep maps a dct by recursively evaluating values and wrapper values based on type. Any value that is a dict will result in recursion on that value. The results of the recursion will wrapped with a wrapper_lambda keyed by type(dict) if one is specified. Thus dicts are always recursed on and returned as dicts, which can then be wrapped and transformed as needed. This includes the top level dict passed in. + :param dcts_or_dct: one or more multi-level dicts. Pass a singular dict or array of dicts + :param wrapper_lambdas: dict of lambda wrappers, keyed by the type they should wrap. These wrappers can wrap all dict values and the outer dict. They can match the dict class itself or any other class. The wrapper will be looked up using wrapper_lambdas.get([type(value)], get_first(filter(lambda wrapper_key: isinstance(value, wrapper_key), wrapper_lambdas.keys()), deep_map_dict_structure(value)) so that if an exact match isn't found the keys will be searched for a parent class. If no match is found the value is returned as is + :return: If dcts is singular, a singular dict, otherwise an array of dicts + """ + # Map the one or multiple items and return one or multiple items respectively + return to_single_if_one_item( + map(lambda dct_or_obj: + # Map the dict to a transformed dict and type wrap, or if the value is not a dict, just type wrap it + type_wrap( + map_dict_to_dict( + lambda key, value: + # Either recurse on dicts or type_wrap non dicts and return them. + [key, deep_map_dict_structure(value, wrapper_lambdas)], + dct_or_obj) if isinstance(dct_or_obj, dict) else + dct_or_obj, + wrapper_lambdas), + to_list(dcts_or_dct))) + + +def resolve_property(obj, property): + """ + Resolves a property string of an instance as a simple attribute or a simple getter if the property represents a no-arg method + :param obj: + :param property: + :return: + """ + value = getattr(obj, property) + return value() if isfunction(value) else value + + +def map_property(dicts_or_objs, property): + """ + Map the list of dicts or objects to property specified by the property string. attr() will be used to resolve object properties, + and any property that represents a simple getter will be called as a no-arg function + :param list_of_dicts_or_objs: + :param property: a string representing the attribute or dict key + :return: The values of the property for each list or dict + """ + return map(lambda dict_or_obj: + dict_or_obj[property] if isinstance(dict_or_obj, dict) else resolve_property(dict_or_obj, property), + dicts_or_objs) + + +def get_first(list, default): + """ + Return the first result of the list or the default if the list is empty + :param list: non-null list + :param default: + :return: + """ + return (list[:1] or [default])[0] + +def get_first_map(map_lambda, list): + """ + Returns the first mapped item that isn't null + :param map_lambda: + :param list: + :return: + """ + for item in list: + result = map_lambda(item) + if result: + return result + +def if_not_none(obj, do_lambda): + """ + Call the lambda with the given obj if the object is not none. Else return None + :param obj: + :param do_lambda: + :return: + """ + return do_lambda(obj) if obj else None + + +def accumulate(accumulate_lambda, target, list): + """ + TODO this exists in python 3 + A type of aggregate function that begins with a target and a list of items that are mapped to chainable functions + of the target. The first mapped item of list is called on target and then subsequent mapped items are called on the + result of the previous call, + :param map_lambda: Takes the current mapped item of the list as arguments. Returns the next target + :param target: The target object of the first mapped item of list. The result of called the mapped item on the target is the target used + for the next item of the list + :param list: + :return: + """ + if len(list) > 0: + return accumulate(accumulate_lambda, accumulate_lambda(target, list[0]), list[1:]) + else: + return target + +def all_existing_classes_subclass(dct_or_obj, **kwargs): + """ + Checks that all the values in kwargs are classes that subclass/equal the corresponding key value in + dct_or_obj. If dct_or_obj doesn't have one of the keys, that key is ignored. + :param dct_or_obj: + :param kwargs: + :return: True if all match + """ + for key, sub_class in kwargs.items(): + base_class = dct_or_obj.get(key, None) if isinstance(dct_or_obj, dict) else getattr(dct_or_obj, key) + if sub_class and base_class and not issubclass(sub_class, base_class): + return False + return True + +def all_existing_keys_match(dct_or_obj, **kwargs): + """ + Checks that all the key values in matching_dict are values that equal the corresponding key in + dct_or_obj. If either has a null value for the key, it is ignored + :param dct_or_obj: + :param kwargs: + :return: True if all match + """ + for key, value in kwargs.items(): + matching_value = dct_or_obj.get(key, None) if isinstance(dct_or_obj, dict) else getattr(dct_or_obj, key) + if value and matching_value and matching_value != value: + return False + return True + diff --git a/footprint/main/lib/serialization.py b/footprint/main/lib/serialization.py new file mode 100644 index 000000000..3ea2dbb1e --- /dev/null +++ b/footprint/main/lib/serialization.py @@ -0,0 +1,43 @@ +from django.core import serializers +from django.core.serializers import json +from footprint.main.lib.functions import merge, remove_keys, is_list_tuple_or_dict + +__author__ = 'calthorpe_associates' + +def json_serialize(objects): + """ + Serializes by removing the fields property from serialization in favor of a flatter dictionary + """ + # this gives you a list of dicts + raw_data = serializers.serialize('python', objects) + # now extract the inner `fields` dicts + actual_data = cond_deep_flat_map_iterable(raw_data) + # and now dump to JSON + return json.dumps(actual_data) + +def cond_deep_flat_map_iterable(iterable): + if (type(iterable) == dict): + # Flatten the dictionary of the fields key if it exists + flattened_dict = remove_keys( + merge(iterable, iterable.get('fields', {})), + ['fields'] + ) + # Map each key value, recursing into the value if it's a dict or iterable + return dict([(key, cond_deep_flat_map_iterable(value) if is_list_tuple_or_dict(value) else value) + for key, value in flattened_dict.iteritems()]) + else: + return map(lambda item: cond_deep_flat_map_iterable(item) if is_list_tuple_or_dict(item) else item, iterable) + +def json_deserialize(json): + return json.loads(json, to_raw_data) + +def to_raw_data(dct): + model = dct['model'] + pk = dct['pk'] + raw_data = { } + raw_data['model'] = model + raw_data['pk'] = pk + raw_data['fields'] = to_raw_data(remove_keys(dict, ['model', 'pk'])) + return raw_data + + diff --git a/footprint/main/lib/test_functions.py b/footprint/main/lib/test_functions.py new file mode 100644 index 000000000..474b874e0 --- /dev/null +++ b/footprint/main/lib/test_functions.py @@ -0,0 +1,27 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +import unittest +from nose.tools import assert_equal +from footprint.main.lib.functions import remove_keys + +class TestFunctions(unittest.TestCase): + def test_remove_keys(self): + x = dict(foo=dict(bar=dict(car=1))) + assert_equal(len(remove_keys(x, [])), 1) + assert_equal(len(remove_keys(x, ['foo'])), 0) + # Use a two-segment string + assert_equal(len(remove_keys(x, ['foo.bar'])), 1) # foo remains + assert_equal(len(remove_keys(x, ['foo.bar'])['foo']), 0) # bar does not + # Use a * + assert_equal(len(remove_keys(x, ['foo.*.car'])), 1) # foo remains + assert_equal(len(remove_keys(x, ['foo.*.car'])['foo']), 1) # bar remains + assert_equal(len(remove_keys(x, ['foo.*.car'])['foo']['bar']), 0) # car does not diff --git a/footprint/main/management/__init__.py b/footprint/main/management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/management/commands/__init__.py b/footprint/main/management/commands/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/management/commands/clear_future_built_forms.py b/footprint/main/management/commands/clear_future_built_forms.py new file mode 100644 index 000000000..d9a425fd1 --- /dev/null +++ b/footprint/main/management/commands/clear_future_built_forms.py @@ -0,0 +1,50 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +import logging + +from django.core.management.base import BaseCommand + +from footprint.main.models.config.scenario import FutureScenario +from footprint.main.models.keys.keys import Keys + + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + """ + This command clears all layer_selections + """ + option_list = BaseCommand.option_list + ( + make_option('-r', '--resave', action='store_true', default=False, + help='Resave all the config_entities to trigger signals'), + make_option('--scenario', default='', help='String matching a key of or more Scenario to run'), + ) + + def handle(self, *args, **options): + scenarios = FutureScenario.objects.filter(key__contains=options['scenario']) if options[ + 'scenario'] else FutureScenario.objects.all() + for scenario in scenarios: + future_scenario_feature_class = scenario.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE) + for future_scenario_feature in future_scenario_feature_class.objects.exclude(built_form__isnull=True): + future_scenario_feature.built_form = None + future_scenario_feature.save() + + + diff --git a/footprint/main/management/commands/clear_layer_selections.py b/footprint/main/management/commands/clear_layer_selections.py new file mode 100644 index 000000000..43710f4d3 --- /dev/null +++ b/footprint/main/management/commands/clear_layer_selections.py @@ -0,0 +1,49 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +import logging + +from django.core.management.base import BaseCommand + +from footprint.main.models.config.scenario import FutureScenario +from footprint.main.models.presentation.layer_selection import DEFAULT_GEOMETRY, layer_selections_of_config_entity + + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + """ + This command clears all layer_selections + """ + option_list = BaseCommand.option_list + ( + make_option('-r', '--resave', action='store_true', default=False, + help='Resave all the config_entities to trigger signals'), + make_option('--scenario', default='', help='String matching a key of or more Scenario to run'), + ) + + def handle(self, *args, **options): + scenarios = FutureScenario.objects.filter(key__contains=options['scenario']) if options[ + 'scenario'] else FutureScenario.objects.all() + for scenario in scenarios: + for layer_selection in layer_selections_of_config_entity(scenario): + # Set the bounds (performs a save) + layer_selection.bounds = DEFAULT_GEOMETRY + + + diff --git a/footprint/main/management/commands/create_user.py b/footprint/main/management/commands/create_user.py new file mode 100644 index 000000000..023a8b331 --- /dev/null +++ b/footprint/main/management/commands/create_user.py @@ -0,0 +1,67 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +import logging +from django.contrib.auth.models import User, Permission +from django.core.management.base import BaseCommand +from tastypie.models import ApiKey +from footprint.main.lib.functions import get_single_value_or_create +from footprint.main.management.commands.footprint_init import all_config_entities +from footprint.main.publishing import tilestache_publishing + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + """ + This command clears all layer_selections + """ + option_list = BaseCommand.option_list + ( + make_option('--username', default='', help='The username'), + make_option('--password', default='', help='The password'), + make_option('--email', default='', help='The email'), + ) + + def handle(self, *args, **options): + if options['username'] and options['password'] and options['email']: + self.user(options['username'], options['password'], options['email']) + for config_entity in all_config_entities(): + tilestache_publishing.on_config_entity_post_save_tilestache(None, instance=config_entity) + else: + raise Exception("Required: --username, --password, and --email") + + def user(self, username, password, email, api_key=None): + """ + Create a user. + :return: + """ + + def create(): + user = User.objects.create_user(username, email, password) + # Make sure the user has permission to update everything for testing purposes + user.user_permissions.add(*list(Permission.objects.all())) + return user + # An api key is created upon creating a user + + user = get_single_value_or_create(User.objects.filter(username=username), create) + api_key_instance = ApiKey.objects.get_or_create(user=user)[0] + if api_key_instance.key != api_key: + api_key_instance.key = api_key + api_key_instance.save() + return {'user': user, 'api_key': api_key_instance} + diff --git a/footprint/main/management/commands/export_geography.py b/footprint/main/management/commands/export_geography.py new file mode 100644 index 000000000..d4108136c --- /dev/null +++ b/footprint/main/management/commands/export_geography.py @@ -0,0 +1,34 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +from django.core.management.base import BaseCommand +from footprint.main.models import Layer +from footprint.main.models.database.information_schema import sync_geometry_columns +from footprint.main.publishing.data_export_publishing import export_layer, export_scenario + + +class Command(BaseCommand): + + option_list = BaseCommand.option_list + ( + make_option('-n', '--schema', help='The source database schema key (e.g. bayarea)'), + ) + + def handle(self, *args, **options): + sync_geometry_columns(None, options.get('schema', None)) + export_layer(request=None, layer_id=5, api_key="TEST_API_KEY") + diff --git a/footprint/main/management/commands/export_scenario.py b/footprint/main/management/commands/export_scenario.py new file mode 100644 index 000000000..c904010fc --- /dev/null +++ b/footprint/main/management/commands/export_scenario.py @@ -0,0 +1,46 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +import logging + +from django.core.management.base import BaseCommand + +from footprint.main.models.config.scenario import FutureScenario +from footprint.main.publishing.data_export_publishing import export_scenario + + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + """ + This command clears all layer_selections + """ + option_list = BaseCommand.option_list + ( + make_option('--scenario', default='', help='String matching a key of or more Scenario to run'), + ) + + def handle(self, *args, **options): + scenarios = FutureScenario.objects.filter(key__contains=options['scenario']) if options[ + 'scenario'] else FutureScenario.objects.all() + for scenario in scenarios: + logger.info("Exporting scenario {0}".format(scenario.key)) + export_scenario(None, scenario.id) + + + diff --git a/footprint/main/management/commands/footprimporter.py b/footprint/main/management/commands/footprimporter.py new file mode 100644 index 000000000..8a5332c45 --- /dev/null +++ b/footprint/main/management/commands/footprimporter.py @@ -0,0 +1,44 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +from django.core.management.base import BaseCommand +from footprint.main.database.import_data import ImportData + +class Command(BaseCommand): + """ + The Footprint importer (footprimporter) is used to synchronize a local system to the data of remote source. There are two import situations. The first is importing from "raw" database tables that are formatted for the UrbanFootprint beta (0.*). This is the beta schema which uses input_outputs_STUDY_AREA where the study area is the old representation of project. The second import situation is to import from a UrbanFootprint 1.* database, where the source schemas match the structure of the new. Use the -v option and specify 0 for the beta schema and 1 for the 1.* version. The default is the beta for now. + """ + option_list = BaseCommand.option_list + ( + make_option('--db', help='A database settings key. Use this instead of host, port, user, password, and database'), + make_option('--host', default='localhost', help='The source database host name'), + make_option('-P', '--port', default='', help='The source database port'), + make_option('-U', '--user', help='The source database user name'), + make_option('-p', '--password', help='The source database password'), + make_option('-d', '--database', help='The source database name'), + make_option('--config_entity', help='The ConfigEntity key of the ConfigEntity whose feature tables are to be imported'), + make_option('--db_entity_key', help='The db_entity_key for which to import feature tables. If not specified all DbEntities that map to a Feature class will import their table'), + make_option('--test', action='store_true', default=False, help='If set only a portion of the geographic data is copied from the Feature table to save time and space. Default all is copied. If the code will first look for a version of the Feature table with "_sample" appended. It will import this if found and otherwise a portion of the full table will be cropped'), + make_option('--dump_only', action='store_true', default=False, help='If set only table dumps can occur, not selecting rows from the source database tables using db_link. This is useful when the source database is not accessible by the target from within a psql call. This should only be used on an empty target database. Not compatible with --test.') + ) + + def handle(self, *args, **options): + import_data = ImportData(**options) + self.stdout.write("pg_dump connection string: {0}\n".format(import_data.pg_dump_connection)) + self.stdout.write("db_link connection string: {0}\n".format(import_data.db_link_connection)) + self.stdout.write("target database connection string: {0}\n".format(import_data.target_database_connection)) + import_data.run() diff --git a/footprint/main/management/commands/footprint_init.py b/footprint/main/management/commands/footprint_init.py new file mode 100644 index 000000000..8e5506d9a --- /dev/null +++ b/footprint/main/management/commands/footprint_init.py @@ -0,0 +1,449 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +import logging +from cProfile import Profile +from django.core.management import call_command +from django.core.management.base import BaseCommand +from django.db import DatabaseError +from footprint.client.configuration.fixture import ConfigEntitiesFixture +from footprint.main.initialization.data_provider import DataProvider + +from footprint.main.lib.functions import merge +from footprint.main.models import DbEntityInterest +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.models.config.global_config import GlobalConfig +from footprint.main.models.config.region import Region +from footprint.main.models.config.project import Project +from footprint.main.models.database.information_schema import InformationSchema, PGNamespace +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.layer import Layer +from footprint.main.models.geospatial.db_entity import DbEntity +from footprint.main.models.application_initialization import application_initialization, initialize_default_media, recalculate_project_bounds +from footprint.main.models.config.global_config import global_config_singleton +from footprint.main.models.config.scenario import BaseScenario, FutureScenario, Scenario +from footprint.main.models.presentation.layer_selection import get_or_create_dynamic_layer_selection_class_and_table, drop_layer_selection_table +from footprint.client.configuration import resolve_fixture, InitFixture +from footprint import settings + + +from footprint.main.publishing import data_import_publishing, layer_publishing, result_publishing, analysis_module_publishing, built_form_publishing, tilestache_publishing, db_entity_publishing, policy_publishing + +from footprint.main.publishing.config_entity_publishing import post_save_config_entity_db_entities, post_save_config_entity_initial, post_save_config_entity_layers +from footprint.main.publishing.data_import_publishing import on_config_entity_post_save_data_import +from footprint.main.publishing.db_entity_publishing import update_or_create_db_entity_and_interest +from footprint.main.publishing.layer_publishing import update_or_create_layer, clone_or_update_cloned_layer +from footprint.main.utils.dynamic_subclassing import create_tables_for_dynamic_classes, drop_tables_for_dynamic_classes +from footprint.main.utils.utils import resolve_model, map_property_path +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator + +logger = logging.getLogger(__name__) + +class Command(BaseCommand): + """ + This command initializes/syncs the footprint server with default and sample data. I'd like this to happen + automatically in response to the post_syncdb event, but that event doesn't seem to fire + (see management/__init__.py for the attempted wiring) + """ + option_list = BaseCommand.option_list + ( + # The following allow you to turn off publishers that are part of the initialization + make_option('--nolayer', action='store_true', default=False, help='Skips layer publisher'), + make_option('--noimport', action='store_true', default=False, help='Skips data import publisher'), + make_option('--noinitializer', action='store_true', default=False, help='Skips initializer publishers'), + make_option('--notilestache', action='store_true', help='Skips tilestache publisher'), + make_option('--nobuilt_form', action='store_true', help='Skips built_form publisher'), + make_option('--nosproutore', action='store_true', help='Skips sproutcore publisher'), + make_option('--noresult', action='store_true', help='Skips result publisher'), + make_option('--nodb_entity', action='store_true', help='Skips db_entity publisher'), + + # If skip is not specified, the full application initialization will occur + # Use skip with the options below to limit initialization + make_option('--skip', action='store_true', default=False, + help='Skip initialization and data creation (for just doing resave)'), + # Use this to skip config_entities and save all db_entity_interests directly + make_option('--save_db_entity_interests', action='store_true', default=False, + help="Saves the db_entity_interests directly to run their publishers, instead of going through the config_entities"), + + make_option('--recreate', action='store_true', default=False, + help='Deletes model instances prior to anything else'), + make_option('--resave', action='store_true', default=False, + help='Resave all the config_entities to trigger signals'), + make_option('--reimport', action='store_true', default=False, + help='Delete imported feature tables and reimport'), + make_option('--recalculate_bounds', action='store_true', default=False, + help='Recalculates the project bounds'), + + make_option('--initializer', action='store_true', default=False, + help='Rerun application initializers'), + make_option('--tilestache', action='store_true', default=False, + help='Explicitly run tilestache publisher'), + make_option('--result', action='store_true', default=False, help='Explicitly run result publisher'), + make_option('--policy', action='store_true', default=False, help='Explicitly run policy publisher'), + make_option('--import', action='store_true', default=False, help="Explicitly run data_import publisher"), + make_option('--db_entity', action='store_true', default=False, + help='Rerun through default db_entities to pick up configuration changes'), + make_option('--built_form', action='store_true', default=False, help='Explicitly run built_form publisher'), + make_option('--layer', action='store_true', default=False, help='Explicitly run layer publisher'), + make_option('--analysis', action='store_true', default=False, help='Explicitly run analysis publisher'), + make_option('--recreate_layer', action='store_true', default=False, help='Delete all layers and then explicitly run layer publisher'), + make_option('--selections', action='store_true', help='Reset config_enitty selections', default=False), + + make_option('--test_clone_scenarios', action='store_true', help='Clone scenarios to simulate a UI clone', default=False), + make_option('--test_upload_layers', action='store_true', help='Upload layers to simulate a UI upload', default=False), + make_option('--test_layer_from_selection', action='store_true', help='Tests creating a layer/db_entity from from a selection', default=False), + make_option('--inspect', action='store_true', help='Specify DbEntity key(s) with db_entity_keys to inspect feature class info about it/them'), + + # Limit which projects and scenarios are acted upon + # TODO not implemented + make_option('--projects', help='Comma separated project key list to init. The default it all'), + make_option('--scenarios', help='Comma separated scenario key list to init. The default is all'), + + # Limit which db_entities are acted on, where aplicable + make_option('--db_entity_keys', help='Comma separated db_entity key list to init to limit data import to given keys'), + + make_option('--recycle', action='store_true', default=False, help='Delete config_entities marked deleted'), + make_option('--delete_clones', action='store_true', default=False, help='Deletes cloned scenarios, db_entities, and layers'), + + make_option('--relayer_selection', action='store_true', help='Recreates all layer selections'), + + make_option('--class', help='Comma separated classes in the form FutureScenario, Project, etc'), + make_option('--profile', action='store_true', help='Profile the command', default=False), + make_option('--memory', action='store_true', help='Profile the command', default=False) + ) + + def handle(self, *args, **options): + + if options['profile']: + profiler = Profile() + profiler.runcall(self._handle, *args, **options) + profiler.print_stats() + else: + self._handle(*args, **options) + + + def _handle(self, *args, **options): + project_keys = options.get('projects', []).split(',') if options['projects'] else None + scenario_keys = options.get('scenarios', []).split(',') if options['scenarios'] else None + db_entity_keys = options.get('db_entity_keys', []).split(',') if options['db_entity_keys'] else None + + project_filter = dict(key__in=project_keys) if project_keys else dict() + scenario_filter = merge( + dict(parent_config_entity__key__in=project_keys) if project_keys else dict(), + dict(key__in=scenario_keys) if scenario_keys else dict()) + limit_to_classes = map( + lambda cls: resolve_model('main.%s' % cls), (options['class'].split(',') if options['class'] else []) + ) + + # Perforance testing + if options['memory']: + ConfigEntity.init_heapy() + ConfigEntity.start_heapy_diagnosis() + + # Delete all ConfigEntity intances so they can be recreated. + # This will cascade delete related models, but it doesn't delete + # BuiltForms and other independent models + if options['recreate']: + for cls in filter_classes(limit_to_classes): + cls.objects.all().delete() + + # Delete deleted config_entities + if options['recycle']: + for cls in filter_classes(limit_to_classes): + cls.objects.filter(deleted=True).delete() + if options['delete_clones']: + for cls in filter_classes(limit_to_classes): + all_config_entities = cls.objects.all() + for config_entity in all_config_entities: + db_entities = filter( + lambda db_entity: db_entity.feature_class_configuration and db_entity.feature_class_configuration.get('generated', None), + map( + lambda db_entity_interest: db_entity_interest.db_entity, + DbEntityInterest.objects.filter(config_entity=config_entity))) + Layer.objects.filter(presentation__config_entity=config_entity, db_entity_key__in=map(lambda db_entity: db_entity.key, db_entities)).delete() + for db_entity in db_entities: + feature_class = FeatureClassCreator(config_entity, db_entity).dynamic_feature_class() + if InformationSchema.objects.table_exists(db_entity.schema, db_entity.table): + drop_tables_for_dynamic_classes(feature_class.__base__, feature_class) + db_entity.delete() + cloned_config_entities = cls.objects.filter(origin_config_entity__isnull=False) + for config_entity in cloned_config_entities: + PGNamespace.objects.drop_schema(config_entity.schema()) + cloned_config_entities.delete() + + if options['save_db_entity_interests']: + # Save just existing db_entity_interests--invoke dependent publishers + for config_entity in filter_config_entities(limit_to_classes): + for db_entity_interest in config_entity.owned_db_entity_interests(**dict(db_entity__key__in=db_entity_keys) if db_entity_keys else {}): + db_entity_interest.save() + + else: + # Default, run through everything, creating/updating config_entities and running + # Dependent publishers that are not explictly disabled + if not options['skip']: + if options['nodb_entity']: + for cls in filter_classes(limit_to_classes): + post_save_config_entity_initial.disconnect(db_entity_publishing.on_config_entity_post_save_db_entity, cls, True, + "db_entity_on_config_entity_post_save") + + if options['noimport']: + # Skip long importing + for cls in filter_classes(limit_to_classes): + post_save_config_entity_db_entities.disconnect(data_import_publishing.on_config_entity_post_save_data_import, cls, True, + "data_import_on_config_entity_post_save") + + if options['nolayer']: + for cls in [FutureScenario, BaseScenario, Project, Region]: + post_save_config_entity_db_entities.disconnect(layer_publishing.on_config_entity_post_save_layer, cls, True, + "layer_on_config_entity_post_save") + + if options['noresult']: + for cls in [FutureScenario, BaseScenario, Project, Region]: + post_save_config_entity_db_entities.disconnect(result_publishing.on_config_entity_post_save_result, cls, True, + "result_on_config_entity_post_save") + + if options['notilestache']: + for cls in [FutureScenario, BaseScenario, Project, Region]: + post_save_config_entity_layers.disconnect(tilestache_publishing.on_config_entity_post_save_tilestache, cls, True, + "tilestache_on_config_entity_post_save") + + if options['nobuilt_form']: + # Skip builtform publishing + for cls in [FutureScenario, BaseScenario, Project, Region, GlobalConfig]: + post_save_config_entity_initial.disconnect(built_form_publishing.on_config_entity_post_save_built_form, cls, True, + "built_form_publishing_on_config_entity_post_save") + # If skip is not specified, the full application initialization will occur + # Use skip with the options below to limit initialization + application_initialization(limit_to_classes=limit_to_classes) + else: + # If skip is specified, use one or more of the following options + # to run publishers directly for all or filtered config_entities + + if options['initializer']: + # Redo initializers. This is non-config_entity dependent stuff, + # like default style templates + initialize_default_media() + client_init = resolve_fixture(None, "init", InitFixture, settings.CLIENT) + client_init.populate_models() + + if options['db_entity']: + # Pick up new stuff in the config_entity configurations, namely default db_entities + #for config_entity in filter_config_entities(limit_to_classes): + # db_entity_publishing.update_or_create_db_entities(config_entity) + missing_schemas = filter(lambda x: not x.schema, DbEntity.objects.all()) + if missing_schemas: + logger.error("No schemas for the following db_entities %s" % map_property_path(missing_schemas, 'name')) + + if options['built_form']: + for config_entity in filter_config_entities(limit_to_classes): + built_form_publishing.on_config_entity_post_save_built_form(None, instance=config_entity) + + if options['import']: + for config_entity in filter_config_entities(limit_to_classes): + on_config_entity_post_save_data_import(None, instance=config_entity) + elif options['reimport']: + for config_entity in filter_config_entities(limit_to_classes): + # Use the predelete to drop the tables + data_import_publishing.on_config_entity_pre_delete_data_import(None, instance=config_entity, db_entity_keys=db_entity_keys) + # Reimport the db_entity tables for the specified config_entities/db_entity_keys + data_import_publishing.on_config_entity_post_save_data_import(None, instance=config_entity, db_entity_keys=db_entity_keys) + + if options['analysis']: + for config_entity in filter_config_entities(limit_to_classes): + analysis_module_publishing.on_config_entity_post_save_analysis_modules(None, instance=config_entity) + + if options['layer'] or options['recreate_layer'] or options['relayer_selection']: + for config_entity in filter_config_entities(limit_to_classes): + if options['recreate_layer']: + layers = Layer.objects.filter(**merge(dict(presentation__config_entity=config_entity), + dict(db_entity_key__in=db_entity_keys) if db_entity_keys else {})) + for layer in layers: + # Remove layer_selection classes + layer_selection_class = get_or_create_dynamic_layer_selection_class_and_table(layer, False) + if layer_selection_class: + layer_selection_class.objects.all().delete() + # Delete the layers + layers.delete() + elif options['relayer_selection']: + # Redo just the layer selection tables + for config_entity in filter_config_entities(limit_to_classes, [FutureScenario, BaseScenario]): + FeatureClassCreator(config_entity).ensure_dynamic_models() + layers = Layer.objects.filter(presentation__config_entity=config_entity) + for selection_layer in layers: + try: + # Drop the table + layer_selection_class = get_or_create_dynamic_layer_selection_class_and_table(selection_layer, True) + + if layer_selection_class: + if hasattr(layer_selection_class.features, 'through'): + layer_selection_features_class = layer_selection_class.features.through + drop_layer_selection_table(layer_selection_features_class) + drop_layer_selection_table(layer_selection_class) + + except DatabaseError, e: + logger.warning("Couldn't destroy LayerSelection tables. Maybe the public.layer table no longer exists: %s" % e.message) + # Recreate + get_or_create_dynamic_layer_selection_class_and_table(selection_layer) + layer_publishing.update_or_create_layer_selections(config_entity=None) + layer_publishing.on_config_entity_post_save_layer(None, instance=config_entity, db_entity_keys=db_entity_keys) + + if options['result']: + for config_entity in filter_config_entities(limit_to_classes): + result_publishing.on_config_entity_post_save_result(None, instance=config_entity) + + if options['policy']: + for config_entity in filter_config_entities(limit_to_classes): + policy_publishing.on_config_entity_post_save_policy(None, instance=config_entity) + + if options['tilestache']: + for config_entity in filter_config_entities(limit_to_classes): + tilestache_publishing.on_config_entity_post_save_tilestache(None, + instance=config_entity, + db_entity_keys=db_entity_keys) + + if options['recalculate_bounds']: + recalculate_project_bounds() + + if options['selections']: + for config_entity in filter_config_entities(limit_to_classes): + # TODO need to do other selections? + for db_entity in config_entity.computed_db_entities(): + config_entity.select_db_entity_of_key(db_entity.key, db_entity) + config_entity._no_post_save_publishing = True + config_entity.save() + config_entity._no_post_save_publishing = False + + if options['resave']: + for config_entity in Region.objects.all(): + logger.info('Re-saving region {config_entity}'.format(config_entity=config_entity.name)) + config_entity.save() + for config_entity in Project.objects.filter(**project_filter): + logger.info('Re-saving project {config_entity}'.format(config_entity=config_entity.name)) + config_entity.save() + for config_entity in BaseScenario.objects.filter(**scenario_filter): + logger.info('Re-saving base scenario {config_entity}'.format(config_entity=config_entity.name)) + config_entity.save() + for config_entity in FutureScenario.objects.filter(**scenario_filter): + logger.info('Re-saving future scenario {config_entity}'.format(config_entity=config_entity.name)) + config_entity.save() + + + def test_upload_layer(db_entity_configuration, config_entity): + # Wipe out the previous import data since we're just testing + data_import_publishing.on_config_entity_pre_delete_data_import( + None, instance=config_entity, db_entity_keys=[db_entity_configuration['key']]) + config_entity.db_entities.filter(key=db_entity_configuration['key']).delete() + # Create the db_entity_interest based on the upload configuration + config_entity._no_post_save_db_entity_interest_publishing = True + db_entity_interest = update_or_create_db_entity_and_interest(config_entity, db_entity_configuration)[0] + config_entity._no_post_save_db_entity_interest_publishing = False + # Resave to trigger publishers + # This will cause the data_import publishing to happen and other dependent publishers + db_entity_interest.save() + return Layer.objects.get(presentation__config_entity=config_entity, db_entity_key=db_entity_interest.db_entity.key) + + def test_upload_layers(config_entity): + # Tests layer upload + from footprint.client.configuration.fixture import ConfigEntityFixture + client_fixture = ConfigEntityFixture.resolve_config_entity_fixture(config_entity) + return map(lambda db_entity_configuration: test_upload_layer(db_entity_configuration, config_entity), client_fixture.import_db_entity_configurations()) + + if options['test_upload_layers']: + config_entity = FutureScenario.objects.filter()[0] + test_upload_layers(config_entity) + + if options['test_layer_from_selection']: + config_entity = FutureScenario.objects.filter()[0] + layer = Layer.objects.get(presentation__config_entity=config_entity, db_entity_key=Keys.DB_ABSTRACT_BASE_FEATURE) + # Create the db_entity_interest based on the upload configuration + config_entity._no_post_save_db_entity_interest_publishing = True + db_entity_interest = update_or_create_db_entity_and_interest(config_entity, layer.db_entity)[0] + config_entity._no_post_save_db_entity_interest_publishing = False + # Resave to trigger publishers + # This will cause the data_import publishing to happen and other dependent publishers + db_entity_interest.save() + + + if options['test_clone_scenarios']: + from footprint.client.configuration.fixture import ConfigEntityFixture + data_provider = DataProvider() + scenario = FutureScenario.objects.filter()[0] + # First do a test layer upload. This way we can ensure that non-default db_entities clone + # along with the defaults + cloned_layers = test_upload_layers(scenario) + + import_scenario_configurations = resolve_fixture("config_entity", + "config_entities", + ConfigEntitiesFixture).import_scenarios(scenario) + + for new_scenario_configuration in import_scenario_configurations: + # Wipe out data and instance if it already exists + matches = scenario.__class__.objects.filter(key=new_scenario_configuration['key']) + if matches: + data_import_publishing.on_config_entity_pre_delete_data_import( + None, instance=matches[0]) + matches.delete() + + # Save the scenario to simulate cloning + # Cloning happens because future_scenario is the clone's origin_config_entity + cloned_scenarios = data_provider.scenarios_per_project( + scenario.project, + import_scenario_configurations) + + if options['inspect']: + for config_entity in filter_config_entities(limit_to_classes): + for db_entity_key in db_entity_keys: + db_entities = config_entity.computed_db_entities(key=db_entity_key) + if db_entities.count() == 1: + db_entity = db_entities[0] + feature_class_creator = FeatureClassCreator(config_entity, db_entity) + feature_class = feature_class_creator.dynamic_feature_class() + logger.info("ConfigEntity: %s, DbEntity key: %s, Feature class: %s" % (config_entity.name, db_entity.name, feature_class.__name__)) + feature_class_configuration = feature_class_creator.feature_class_configuration_from_introspection() + logger.info("Feature Class configuration from introspection: %s" % feature_class_configuration) + + call_command('collectstatic', interactive=False) + +def all_config_entities(): + config_entities = ConfigEntity.objects.filter(deleted=False).select_subclasses() + + sort_priority = {GlobalConfig: 1, Region: 2, Project: 3, BaseScenario: 4, FutureScenario: 5} + return sorted(map( + lambda config_entity: resolve_scenario(config_entity) if isinstance(config_entity, Scenario) else config_entity, + config_entities + ), key=lambda config_entity: sort_priority[config_entity.__class__]) + +def resolve_scenario(scenario): + for scenario_type in ['basescenario', 'futurescenario']: + if hasattr(scenario, scenario_type): + return getattr(scenario, scenario_type) + return scenario + +def config_entity_classes(): + return [BaseScenario, FutureScenario] + Scenario.lineage() + +def filter_classes(limit_to_classes): + classes = [GlobalConfig, Region, Project, BaseScenario, FutureScenario] + if len(limit_to_classes)==0: + return classes + return filter(lambda cls: cls in limit_to_classes, classes) + +def filter_config_entities(limit_to_classes, pre_limited_classes=[]): + config_entities = all_config_entities() + if len(limit_to_classes + pre_limited_classes)==0: + return config_entities + return filter(lambda config_entity: issubclass(config_entity.__class__, tuple(limit_to_classes+pre_limited_classes)), config_entities) diff --git a/footprint/main/management/commands/monitor_celery.py b/footprint/main/management/commands/monitor_celery.py new file mode 100644 index 000000000..f2945161f --- /dev/null +++ b/footprint/main/management/commands/monitor_celery.py @@ -0,0 +1,41 @@ +from django.core.management import BaseCommand +from celery import current_app + +__author__ = 'calthorpe_associates' + +class Command(BaseCommand): + def handle(self, *args, **options): + state = current_app.events.State() + print 'Current Tasks: %s' % current_app.tasks.keys() + + def announce_succeeded_tasks(event): + state.event(event) + task_id = event['uuid'] + + print('TASK SUCCEEDED: %s[%s] %s' % ( + event['name'], task_id, state[task_id].info(), )) + + def announce_failed_tasks(event): + state.event(event) + task_id = event['uuid'] + + print('TASK FAILED: %s[%s] %s' % ( + event['name'], task_id, state[task_id].info(), )) + + def announce_dead_workers(event): + state.event(event) + hostname = event.get('hostname', None) + + print('Event type %s received' % event.get('type', 'undefined')) + if hostname and not state.workers[hostname].alive: + print('Worker %s missed heartbeats' % (hostname, )) + + + + with current_app.connection() as connection: + recv = current_app.events.Receiver(connection, handlers={ + 'task-failed': announce_failed_tasks, + 'task-succeeded': announce_succeeded_tasks, + 'worker-heartbeat': announce_dead_workers, + }) + recv.capture(limit=None, timeout=None, wakeup=True) diff --git a/footprint/main/management/commands/refresh_sample_data.py b/footprint/main/management/commands/refresh_sample_data.py new file mode 100644 index 000000000..4d35d96e0 --- /dev/null +++ b/footprint/main/management/commands/refresh_sample_data.py @@ -0,0 +1,53 @@ +from django.conf import settings +from django.core.management import BaseCommand +from sarge import run +from footprint.client.configuration.fixture import ConfigEntityFixture +from footprint.main.database.import_data import ImportData +from footprint.main.models import ConfigEntity, Region, Scenario, Project +from footprint.main.utils.utils import postgres_url_to_connection_dict +import psycopg2 + +__author__ = 'calthorpe' + + +class Command(BaseCommand): + """ + This command connects to the source of the sample data and populates a local db + """ + def drop_db(self): + + # Try to connect + try: + conn = psycopg2.connect("dbname='urbanfootprint' user='calthorpe' password='Calthorpe'") + except Exception, E: + raise E + + cur = conn.cursor() + conn.set_isolation_level(0) + try: + cur.execute("""DROP DATABASE sample_data""") + cur.execute("""CREATE DATABASE sample_data TEMPLATE template_postgis""") + except Exception, E: + raise E + + def handle(self, *args, **options): + self.drop_db() + project = Project.objects.all()[0] + client_fixture = ConfigEntityFixture.resolve_config_entity_fixture(project) + default_db_entity_configurations = client_fixture.default_db_entity_configurations() + for db_entity_config in default_db_entity_configurations: + importer = ImportData(config_entity=project, db_entity=db_entity_config) + importer.target_database = settings.DATABASES['sample_data'] + importer.create_target_db_string() + + # For now we only import data for DbEntity instances with a configured database url + connection_dict = postgres_url_to_connection_dict(db_entity_config['url']) + # The import database currently stores tables as public.[config_entity.key]_[feature_class._meta.db_table (with schema removed)][_sample (for samples)] + # We always use the table name without the word sample for the target table name + source_table = "{0}_{1}_{2}".format(project.key, db_entity_config['table'], 'sample') + importer._dump_tables_to_target('-t %s' % source_table, + source_schema='public', + target_schema='public', + source_table=source_table, + target_table=source_table, + connection_dict=connection_dict) diff --git a/footprint/main/management/commands/sample_base_feature_update.py b/footprint/main/management/commands/sample_base_feature_update.py new file mode 100644 index 000000000..db3ae63fc --- /dev/null +++ b/footprint/main/management/commands/sample_base_feature_update.py @@ -0,0 +1,49 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +from random import randrange +from django.core.management.base import BaseCommand +from footprint.main.models import Project, Placetype +from footprint.main.models.application_initialization import application_initialization, create_data_provider_data +from footprint.main.models.keys.keys import Keys + +class Command(BaseCommand): + """ + This command initializes/syncs the footprint server with default and sample data. I'd like this to happen automatically in response to the post_syncdb event, but that event doesn't seem to fire (see management/__init__.py for the attempted wiring) + """ + option_list = BaseCommand.option_list + ( + make_option('-r', '--resave', action='store_true', default=False, help='Resave all the config_entities to trigger signals'), + make_option('-s', '--skip', action='store_true', default=False, help='Skip initialization and data creation (for just doing resave)'), + make_option('--scenario', default='', help='String matching a key of or more Scenario to run'), + ) + + def handle(self, *args, **options): + if not options['skip']: + application_initialization() + create_data_provider_data() + + projects = Project.objects.filter() + placetypes = Placetype.objects.all() + for project in projects: + base_feature_class = project.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_FEATURE) + base_features = base_feature_class.objects.all() + for base_feature in base_features: + base_feature.built_form = placetypes[randrange(0, len(placetypes)-1)] + base_feature.save() + + diff --git a/footprint/main/management/commands/sample_core_run.py b/footprint/main/management/commands/sample_core_run.py new file mode 100644 index 000000000..0d79c3d8d --- /dev/null +++ b/footprint/main/management/commands/sample_core_run.py @@ -0,0 +1,87 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +from random import randrange +import logging + +from django.contrib.auth.models import User +from django.core.management.base import BaseCommand + +from footprint.main.models import Placetype +from footprint.main.models.analysis_module.core_module.core import executeCore +from footprint.main.models.analysis_module.fiscal_module.fiscal import Fiscal, executeFiscal +from footprint.main.models.analysis_module.vmt_module.vmt import Vmt, executeVmt +from footprint.main.models.application_initialization import application_initialization, create_data_provider_data +from footprint.main.models.config.scenario import FutureScenario, Scenario +from footprint.main.models.keys.keys import Keys + + +logger = logging.getLogger(__name__) + + +class Command(BaseCommand): + """ + This command initializes/syncs the footprint server with default and sample data. I'd like this to happen automatically in response to the post_syncdb event, but that event doesn't seem to fire (see management/__init__.py for the attempted wiring) + """ + option_list = BaseCommand.option_list + ( + make_option('-r', '--resave', action='store_true', default=False, + help='Resave all the config_entities to trigger signals'), + make_option('-s', '--skip', action='store_true', default=False, + help='Skip initialization and data creation (for just doing resave)'), + make_option('--core', action='store_true', default=False, help='Run core'), + make_option('--fiscal', action='store_true', default=False, help='Run fiscal'), + make_option('--vmt', action='store_true', default=False, help='Run VMT'), + make_option('--scenario', default='', help='String matching a key of or more Scenario to run') + ) + + def handle(self, *args, **options): + if not options['skip']: + application_initialization() + create_data_provider_data() + + scenarios = FutureScenario.objects.filter(key__contains=options['scenario']) if options[ + 'scenario'] else FutureScenario.objects.all() + + all_scenarios = Scenario.objects.all() + + user = User.objects.all()[0] + for scenario in scenarios: + if options['core']: + future_scenario_feature_class = scenario.feature_class_of_db_entity_key( + Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE) + future_scenario_features = future_scenario_feature_class.objects.all() + built_forms = Placetype.objects.all() + for scenario_built_form_feature in future_scenario_features.filter()[:100]: + scenario_built_form_feature.built_form = built_forms[randrange(0, len(built_forms) - 1)] + scenario_built_form_feature.dirty = True + scenario_built_form_feature.save() + executeCore(None, user, scenario) + + if options['fiscal']: + fiscal = Fiscal.objects.update_or_create(config_entity=scenario)[0] + fiscal.start(user.id) + + if options['vmt']: + for scenario in all_scenarios: + executeVmt(None, user, scenario) + # vmt = Vmt.objects.update_or_create(config_entity=scenario)[0] + # vmt.start(user.id) + + + + diff --git a/footprint/main/management/commands/sample_primary_base_update.py b/footprint/main/management/commands/sample_primary_base_update.py new file mode 100644 index 000000000..8ae35053e --- /dev/null +++ b/footprint/main/management/commands/sample_primary_base_update.py @@ -0,0 +1,48 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from optparse import make_option +from django.core.management.base import BaseCommand +from footprint.main.initialization.data_provider import DataProvider +from footprint.main.models import Layer +from footprint.main.models.application_initialization import application_initialization, create_data_provider_data +from footprint.main.models.base.primary_parcel_feature import PrimaryParcelFeature +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.layer_selection import get_or_create_dynamic_layer_selection_class_and_table + + +class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option('-s', '--skip', action='store_true', default=False, help='Skip initialization and data creation (for just doing resave)'), + ) + + def handle(self, *args, **options): + if not options['skip']: + application_initialization() + create_data_provider_data() + + user = DataProvider().user()['user'] + scenarios = DataProvider().scenarios() + for scenario in scenarios: + layer_library = scenario.presentation_set.filter(key=Keys.LAYER_LIBRARY_DEFAULT)[0] + presentation_medium = layer_library.presentationmedium_set.get(db_entity_key=Keys.DB_ABSTRACT_PRIMARY_PARCEL_SOURCE) + layer = Layer.objects.get(id=presentation_medium.id) # Cast + layer_selection_class = get_or_create_dynamic_layer_selection_class_and_table(layer) + primary_base_feature_class = scenario.feature_class_of_base_class(PrimaryParcelFeature) + layer_selection = layer_selection_class.objects.get(user=user, layer=layer) + layer_selection.bounds = primary_base_feature_class.objects.all()[0].geography.geometry + layer_selection.save() diff --git a/footprint/main/managers/__init__.py b/footprint/main/managers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/managers/config/__init__.py b/footprint/main/managers/config/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/managers/config/db_entity_interest_manager.py b/footprint/main/managers/config/db_entity_interest_manager.py new file mode 100644 index 000000000..699b3e4b3 --- /dev/null +++ b/footprint/main/managers/config/db_entity_interest_manager.py @@ -0,0 +1,55 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.lib.functions import unique +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.config.interest import Interest +from footprint.main.models.geospatial.db_entity import DbEntity +from footprint.main.models.database.information_schema import InformationSchema +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + +class DbEntityInterestManager(GeoInheritanceManager): + + def sync_db_table_entities(self, config_entity): + """ + Syncs the db_entities representing tables with the tables in the this instance's schema. This should only + be used when tables are added or removed from the system outside of the UrbanFootprint application. + Normally the db_entries will stay synced automatically. No DbEntry instances representing queries, views, + or other table-based representations are added or deleted here. + """ + # Load the db_entities that represent tables + table_entities = config_entity.db_entities.filter(table__isnull=False, query__isnull=True) + table_entity_names = map(lambda table_entity: table_entity.table, table_entities) + # Load the physical tables in the schema + table_names = unique( + map( + lambda information_schema: information_schema.table_name, + InformationSchema.objects.filter(table_schema=config_entity.schema()))) + # Compare table names to find new tables for which to create entries + owner_interest = Interest.objects.get(key=Keys.INTEREST_OWNER) + for new_table_name in set(table_names) - set(table_entity_names): + # Create the DbEntity and join it to the ConfigEntity with an owner DbEntityInterest + table_entity = DbEntity.objects.create(name=new_table_name, schema=config_entity.schema(), table=new_table_name) + self.create(config_entity=config_entity, db_entity=table_entity, interest=owner_interest) + + # Compare table names to find deleted tables for which to delete entries + for table_entity in table_entities: + if not table_entity.table in table_names: + table_entity.delete() + diff --git a/footprint/main/managers/database/__init__.py b/footprint/main/managers/database/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/managers/database/managers.py b/footprint/main/managers/database/managers.py new file mode 100644 index 000000000..7fcd194a8 --- /dev/null +++ b/footprint/main/managers/database/managers.py @@ -0,0 +1,93 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import logging +from django.db import models, transaction +from django.db import connection +import psycopg2 +from footprint import settings +from footprint.common.utils.postgres_utils import build_postgres_conn_string, pg_connection_parameters +from footprint.main.lib.functions import merge, compact_dict + +logger = logging.getLogger(__name__) + +__author__ = 'calthorpe_associates' + +class InformationSchemaManager(models.Manager): + def tables_of_schema(self, schema, **kwargs): + return self.values('table_name').annotate().filter(table_schema=schema, **kwargs) + + def tables_of_schema_with_column(self, schema, column): + return self.values('table_name').annotate().filter(table_schema=schema, column_name=column) + + def columns_of_table(self, schema, table, column_name=None): + """ + Returns the columns matching the given schema, table, and column_name + :param schema: + :param table: + :param column_name: Optionally limit the result to a single value + :return: The matching InformationSchema instances + """ + return self.filter(**compact_dict(dict(table_schema=schema, table_name=table, column_name=column_name))) + + def has_column(self, schema, table, column_name): + """ + Returns true if the give column exists for the given schema and table, otherwise false + :param schema: + :param table: + :param column_name: + :return: + """ + return len(self.columns_of_table(schema, table, column_name)) == 1 + + def table_exists(self, schema, table): + return len(self.filter(table_schema=schema, table_name=table)) > 0 + + def tables_with_geometry(self, schema=None, table=None): + """ + Returns tables with a column data type of 'geometry' + :param schema: Optional schema to search + :param table: Optional table to which to limit search. This guarantees 0 or 1 result + :return: + """ + return self.filter(**merge(dict(udt_name='geometry'), + compact_dict(dict(table_schema=schema, table_name=table)))) + +class PGNamespaceManager(models.Manager): + def schema_exists(self, schema): + return len(self.filter(nspname=schema)) > 0 + + def create_schema(self, schema, connection=connection): + if not self.schema_exists(schema): + logger.debug("Creating schema %s" % schema) + + + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = connection.cursor() + cursor.execute('create schema {0}'.format(schema)) + logger.debug("Schema %s created" % schema) + + def drop_schema(self, schema, connection=connection): + if self.schema_exists(schema): + logger.debug("Dropping schema %s" % schema) + + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = connection.cursor() + cursor.execute('drop schema {0} cascade'.format(schema)) + logger.debug("Schema %s dropped" % schema) diff --git a/footprint/main/managers/geo_inheritance_manager.py b/footprint/main/managers/geo_inheritance_manager.py new file mode 100644 index 000000000..e65abe7f1 --- /dev/null +++ b/footprint/main/managers/geo_inheritance_manager.py @@ -0,0 +1,117 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db.models import GeoManager +from django.contrib.gis.db.models.query import GeoQuerySet +from django.db import transaction, IntegrityError +from model_utils.managers import InheritanceManager, InheritanceQuerySet +from django.contrib.gis.geos import Polygon +from footprint.main.lib.functions import dual_map_to_dict + +__author__ = 'calthorpe_associates' + + +class GeoInheritanceQuerySet(GeoQuerySet, InheritanceQuerySet): + + def result_fields_and_title_lookup(self): + + # Find the feature_class parent field so we can remove it from the result fields. + parent_fields = map(lambda field: field.name + '_id', self.model._meta.parents.values()) + omit_fields = parent_fields+['wkb_geometry'] + + # Call query_result.values() to gain access to the field names. + values_query_result = self if hasattr(self, 'field_names') else self.values() + result_fields = filter(lambda field_name: field_name not in omit_fields, values_query_result.field_names) + return ( + result_fields, + # Find normal field titles + #titles = map(lambda tup: self.cleanup_title(tup[1]), values_query_result.query.select) + # Create a lookup from field name to title + dual_map_to_dict(lambda key, value: [key, value], result_fields, result_fields)) + + def extent_polygon(self): + """ + Convert extent into something more useful--a simple geos polygon + """ + try: + # This seems to raise if no rows exist + extent = self.extent() + except: + return None + bounds = Polygon(( + (extent[0], extent[1]), + (extent[0], extent[3]), + (extent[2], extent[3]), + (extent[2], extent[1]), + (extent[0], extent[1]), + )) + return bounds + + +class FootprintGeoManager(GeoManager): + # http://djangosnippets.org/snippets/1114/ + def update_or_create(self, **kwargs): + """ + updates, creates or gets based on the kwargs. Works like get_or_create but in addition will update + the kwargs specified in defaults and returns a third value to indicate if an update happened + :param kwargs: + :return: + """ + assert kwargs, 'update_or_create() must be passed at least one keyword argument' + obj, created = self.get_or_create(**kwargs) + defaults = kwargs.pop('defaults', {}) + if created: + return obj, True, False + else: + try: + params = dict([(k, v) for k, v in kwargs.items() if '__' not in k]) + params.update(defaults) + for attr, val in params.items(): + if hasattr(obj, attr): + setattr(obj, attr, val) + sid = transaction.savepoint() + obj.save(force_update=True) + transaction.savepoint_commit(sid) + return obj, False, True + except IntegrityError, e: + transaction.savepoint_rollback(sid) + try: + return self.get(**kwargs), False, False + except self.model.DoesNotExist: + raise e + + +class GeoInheritanceManager(FootprintGeoManager, InheritanceManager): + """ + Combines the GeoManager and Inheritance Managers into one. The get_query_set is overridden below to return a + class that combines the two QuerySet subclasses + """ + + def get_query_set(self): + return GeoInheritanceQuerySet(self.model) + + def __getattr__(self, attr, *args): + """ + Pass unknown methods to the QuerySetManager + :param attr: + :param args: + :return: + """ + try: + return getattr(self.__class__, attr, *args) + except AttributeError: + return getattr(self.get_query_set(), attr, *args) diff --git a/footprint/main/managers/managers.py b/footprint/main/managers/managers.py new file mode 100644 index 000000000..35b5821fd --- /dev/null +++ b/footprint/main/managers/managers.py @@ -0,0 +1,150 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import psycopg2 + +from footprint.main.lib.functions import map_to_2d_dict, first +from uf_tools import executeSQL_now + +__author__ = 'calthorpe_associates' + +# Table-level calls for the model classes +from django.db.models.aggregates import Sum, Avg +from django.db import models + + +# class CoreGridManager(models.Manager): +# +# def placetype_notnull(self): +# return self.filter(placetype_id__isnull=False) +# +# def sum_dwelling_units_and_employment(self): +# result = self.aggregate(Sum('pop'), Sum('du'), Sum('emp')) +# return map(lambda key: result[key], ['pop__sum', 'du__sum', 'emp__sum']) +# +# def sum_population(self): +# return self.placetype_notnull().aggregate(Sum('pop')).values +# +# def sum_acres_parcel(self): +# return self.placetype_notnull().aggregate(Sum('acres_parcel')).values +# +# def avg_gross_residential_density(self): +# return self.placetype_notnull().filter(gross_res_dens__gt=0).aggregate(Avg('gross_res_dens')).values +# +# def avg_gross_employment_density(self): +# return self.placetype_notnull().filter(gross_emp_dens__gt=0).aggregate(Avg('gross_emp_dens')).values +# +# def avg_net_residential_density(self): +# return self.placetype_notnull().filter(net_res_dens__gt=0).aggregate(Avg('net_res_dens')).values +# +# def avg_net_employment_density(self): +# return self.placetype_notnull().filter(net_emp_dens__gt=0).aggregate(Avg('net_emp_dens')).values +# +# class HorizDevelopableManager(models.Manager): +# +# def placetype_notnull(self): +# return self.filter(placetype_id__isnull=False) +# +# def sum_dwelling_units_and_employment(self): +# result = self.placetype_notnull().aggregate(Sum('pop_res_hz'), Sum('du_hz'), Sum('emp_hz')) +# return map(lambda key: result[key], ['pop_res_hz__sum', 'du_hz__sum', 'emp_hz__sum']) +# +# def sum_new_units(self): +# result = self.placetype_notnull().aggregate(Sum('du_detsf_ll_hz'), Sum('du_detsf_sl_hz'), Sum('du_attsf_hz'), Sum('du_mf_hz')) +# return map(lambda key: result[key], ['du_detsf_ll_hz__sum', 'du_detsf_sl_hz__sum', 'du_attsf_hz__sum', 'du_mf_hz__sum']) +# +# def sum_new_jobs(self): +# result = self.placetype_notnull().aggregate(Sum('emp_ret_hz'), Sum('emp_off_hz'), Sum('emp_educ_hz'), Sum('emp_ind_hz'), Sum('emp_manuf_hz')) +# return [result['emp_ret_hz__sum'], result['emp_off_hz__sum']+result['emp_educ_hz__sum'], result['emp_ind_hz__sum']+result['emp_manuf_hz__sum']] +# +# +# class IncrementManager(models.Manager): +# +# def init(self, scenario): +# self.scenario = scenario +# +# def sum_dwelling_units_and_employment(self): +# result = self.aggregate(Sum('pop_inc'), Sum('du_inc'), Sum('emp_inc')) +# return map(lambda key: result[key], ['pop_inc__sum', 'du_inc__sum', 'emp_inc__sum']) +# +# def placetype_notnull(self): +# return self.filter(placetype_id__isnull=False) +# +# def sum_new_units(self): +# result = self.aggregate(Sum('du_detsf_ll_inc'), Sum('du_detsf_sl_inc'), Sum('du_attsf_inc'), Sum('du_mf_inc')) +# return map(lambda key: result[key], ['du_detsf_ll_inc__sum', 'du_detsf_sl_inc__sum', 'du_attsf_inc__sum', 'du_mf_inc__sum']) +# +# def sum_new_jobs(self): +# result = self.aggregate(Sum('emp_ret_inc'), Sum('emp_off_inc'), Sum('emp_ind_inc')) +# return [result['emp_ret_inc__sum'], result['emp_off_inc__sum'], result['emp_ind_inc__sum']] +# +# def sum_new_residential_by_ldc(self): +# return self.sum_new_by_ldc('du_inc') +# +# def sum_new_employment_by_ldc(self): +# return self.sum_new_by_ldc('emp_inc') +# +# def sum_new_units_join_base(self, where): +# result = self.aggregate(Sum('du_inc')) +# return [result['du_inc__sum']] +# +# def sum_transit_corridor_du_increment(self): +# return self.query_increment("du", 'transit_corridor') +# +# def sum_transit_corridor_emp_increment(self): +# return self.query_increment("emp", 'transit_corridor') +# +# def sum_hsr_station_du_increment(self): +# return self.query_increment("du", 'hsr_station') +# +# def sum_hsr_station_emp_increment(self): +# return self.query_increment("emp", 'hsr_station') +# +# def sum_new_by_ldc(self, sum_column): +# densities = ['urban_ldc', 'compact_ldc', 'standard_ldc'] +# results = filter( +# # Get all results with one of the densities true +# lambda result: first(lambda density: result.get(density, None), densities) and result['refill'] in [0, 1], +# self.values(*(densities + ['refill'])).annotate(Sum(sum_column))) +# # Map the results by density and then refill +# dictionary = map_to_2d_dict( +# lambda result: 'refill' if result['refill'] == 1 else 'greenfield', +# lambda result: [first(lambda key: result.get(key, None), densities), result["{0}__sum".format(sum_column)]], +# results +# ) +# # Fill missing values +# for type in ['refill', 'greenfield']: +# if not dictionary.get(type, None): +# dictionary[type] = {} +# for density in densities: +# if not dictionary[type].get(density, None): +# dictionary[type][density] = 0 +# return dictionary +# +# def query_increment(self, column_prefix, transit_column): +# formatted_transit_column = 'base_grid__{0}'.format(transit_column) +# formatted_sum_column = '{0}_inc'.format(column_prefix) +# results = self.values(formatted_transit_column).order_by(formatted_transit_column).annotate(Sum(formatted_sum_column)) +# +# # The first row is the non-null result, the second is the null result +# return map(lambda result: result["{0}__sum".format(formatted_sum_column)], results) +# +# class BaseYearGridManager(models.Manager): +# pass +# +# class DevelopableAcresManager(models.Manager): +# pass diff --git a/footprint/main/migrations/0001_initial.py b/footprint/main/migrations/0001_initial.py new file mode 100644 index 000000000..9006d178b --- /dev/null +++ b/footprint/main/migrations/0001_initial.py @@ -0,0 +1,1591 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'BuildingUsePercent' + db.create_table('main_buildingusepercent', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('percent', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=21, decimal_places=20)), + ('building_attributes', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.BuildingAttributeSet'])), + ('building_use_definition', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.BuildingUseDefinition'])), + ('vacancy_rate', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=4, decimal_places=3)), + ('household_size', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=5, decimal_places=3)), + ('efficiency', self.gf('django.db.models.fields.DecimalField')(default=0.85, max_digits=6, decimal_places=4)), + ('square_feet_per_unit', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=11, decimal_places=3)), + ('floor_area_ratio', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=12, decimal_places=10)), + ('unit_density', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=16, decimal_places=10)), + ('gross_built_up_area', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=13, decimal_places=3)), + ('net_built_up_area', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=13, decimal_places=3)), + )) + db.send_create_signal('main', ['BuildingUsePercent']) + + # Adding model 'BuildingUseDefinition' + db.create_table('main_buildingusedefinition', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('category', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.BuildingUseDefinition'], null=True)), + )) + db.send_create_signal('main', ['BuildingUseDefinition']) + + # Adding model 'BuildingAttributeSet' + db.create_table('main_buildingattributeset', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('parking_spaces', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=7, decimal_places=3)), + ('parking_structure_square_feet', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('floors', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=7, decimal_places=3)), + ('total_far', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=10, decimal_places=7)), + ('gross_population_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=14, decimal_places=10)), + ('household_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=14, decimal_places=10)), + ('impervious_roof_percent', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=5, decimal_places=3)), + ('impervious_hardscape_percent', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=5, decimal_places=3)), + ('pervious_hardscape_percent', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=5, decimal_places=3)), + ('softscape_and_landscape_percent', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=5, decimal_places=3)), + ('irrigated_percent', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=5, decimal_places=3)), + ('hardscape_percent', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=5, decimal_places=3)), + ('residential_irrigated_square_feet', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=9, decimal_places=2)), + ('commercial_irrigated_square_feet', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=9, decimal_places=2)), + ('residential_average_lot_size', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=9, decimal_places=2)), + ('gross_net_ratio', self.gf('django.db.models.fields.DecimalField')(default=1, max_digits=8, decimal_places=7)), + ('combined_pop_emp_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=4)), + )) + db.send_create_signal('main', ['BuildingAttributeSet']) + + # Adding model 'Tag' + db.create_table('main_tag', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('tag', self.gf('django.db.models.fields.CharField')(max_length=100)), + )) + db.send_create_signal('main', ['Tag']) + + # Adding model 'Medium' + db.create_table('main_medium', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=120)), + ('url', self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True)), + ('content_type', self.gf('django.db.models.fields.CharField')(max_length=20, null=True, blank=True)), + ('content', self.gf('picklefield.fields.PickledObjectField')(null=True)), + )) + db.send_create_signal('main', ['Medium']) + + # Adding model 'BuiltFormExample' + db.create_table('main_builtformexample', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=120)), + ('url_aerial', self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True)), + ('url_street', self.gf('django.db.models.fields.CharField')(max_length=200, null=True, blank=True)), + ('content', self.gf('picklefield.fields.PickledObjectField')(null=True)), + )) + db.send_create_signal('main', ['BuiltFormExample']) + + # Adding model 'BuiltForm' + db.create_table('main_builtform', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('building_attributes', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.BuildingAttributeSet'], null=True)), + ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=120)), + ('origin_built_form', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.BuiltForm'], null=True)), + ('medium', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Medium'], null=True)), + )) + db.send_create_signal('main', ['BuiltForm']) + + # Adding M2M table for field tags on 'BuiltForm' + db.create_table('main_builtform_tags', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('builtform', models.ForeignKey(orm['main.builtform'], null=False)), + ('tag', models.ForeignKey(orm['main.tag'], null=False)) + )) + db.create_unique('main_builtform_tags', ['builtform_id', 'tag_id']) + + # Adding M2M table for field media on 'BuiltForm' + db.create_table('main_builtform_media', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('builtform', models.ForeignKey(orm['main.builtform'], null=False)), + ('medium', models.ForeignKey(orm['main.medium'], null=False)) + )) + db.create_unique('main_builtform_media', ['builtform_id', 'medium_id']) + + # Adding M2M table for field examples on 'BuiltForm' + db.create_table('main_builtform_examples', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('builtform', models.ForeignKey(orm['main.builtform'], null=False)), + ('builtformexample', models.ForeignKey(orm['main.builtformexample'], null=False)) + )) + db.create_unique('main_builtform_examples', ['builtform_id', 'builtformexample_id']) + + # Adding model 'DbEntity' + db.create_table('main_dbentity', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('key', self.gf('django.db.models.fields.CharField')(max_length=50)), + ('schema', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)), + ('table', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)), + ('srid', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)), + ('creator', self.gf('django.db.models.fields.related.ForeignKey')(related_name='db_entity_creator', null=True, to=orm['auth.User'])), + ('updater', self.gf('django.db.models.fields.related.ForeignKey')(related_name='db_entity_updater', null=True, to=orm['auth.User'])), + ('feature_class_configuration', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('extent_authority', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('url', self.gf('django.db.models.fields.CharField')(max_length=1000, null=True)), + ('hosts', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('query', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('class_key', self.gf('django.db.models.fields.CharField')(max_length=50, null=True)), + ('group_by', self.gf('picklefield.fields.PickledObjectField')(null=True)), + )) + db.send_create_signal('main', ['DbEntity']) + + # Adding M2M table for field tags on 'DbEntity' + db.create_table('main_dbentity_tags', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('dbentity', models.ForeignKey(orm['main.dbentity'], null=False)), + ('tag', models.ForeignKey(orm['main.tag'], null=False)) + )) + db.create_unique('main_dbentity_tags', ['dbentity_id', 'tag_id']) + + # Adding model 'Category' + db.create_table('main_category', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('key', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('value', self.gf('django.db.models.fields.CharField')(max_length=100)), + )) + db.send_create_signal('main', ['Category']) + + # Adding model 'BuiltFormSet' + db.create_table('main_builtformset', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=120)), + )) + db.send_create_signal('main', ['BuiltFormSet']) + + # Adding M2M table for field built_forms on 'BuiltFormSet' + db.create_table('main_builtformset_built_forms', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('builtformset', models.ForeignKey(orm['main.builtformset'], null=False)), + ('builtform', models.ForeignKey(orm['main.builtform'], null=False)) + )) + db.create_unique('main_builtformset_built_forms', ['builtformset_id', 'builtform_id']) + + # Adding model 'Policy' + db.create_table('main_policy', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('key', self.gf('django.db.models.fields.CharField')(max_length=50)), + ('schema', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)), + ('values', self.gf('picklefield.fields.PickledObjectField')()), + )) + db.send_create_signal('main', ['Policy']) + + # Adding M2M table for field tags on 'Policy' + db.create_table('main_policy_tags', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('policy', models.ForeignKey(orm['main.policy'], null=False)), + ('tag', models.ForeignKey(orm['main.tag'], null=False)) + )) + db.create_unique('main_policy_tags', ['policy_id', 'tag_id']) + + # Adding M2M table for field policies on 'Policy' + db.create_table('main_policy_policies', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('from_policy', models.ForeignKey(orm['main.policy'], null=False)), + ('to_policy', models.ForeignKey(orm['main.policy'], null=False)) + )) + db.create_unique('main_policy_policies', ['from_policy_id', 'to_policy_id']) + + # Adding model 'PolicySet' + db.create_table('main_policyset', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=120)), + )) + db.send_create_signal('main', ['PolicySet']) + + # Adding M2M table for field policies on 'PolicySet' + db.create_table('main_policyset_policies', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('policyset', models.ForeignKey(orm['main.policyset'], null=False)), + ('policy', models.ForeignKey(orm['main.policy'], null=False)) + )) + db.create_unique('main_policyset_policies', ['policyset_id', 'policy_id']) + + # Adding model 'ConfigEntity' + db.create_table('main_configentity', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('key', self.gf('django.db.models.fields.CharField')(max_length=120)), + ('scope', self.gf('django.db.models.fields.CharField')(max_length=120)), + ('bounds', self.gf('django.contrib.gis.db.models.fields.MultiPolygonField')()), + ('creator', self.gf('django.db.models.fields.related.ForeignKey')(related_name='config_entity_creator', null=True, to=orm['auth.User'])), + ('updater', self.gf('django.db.models.fields.related.ForeignKey')(related_name='config_entity_updater', null=True, to=orm['auth.User'])), + ('parent_config_entity', self.gf('django.db.models.fields.related.ForeignKey')(related_name='parent_set', null=True, to=orm['main.ConfigEntity'])), + ('origin_config_entity', self.gf('django.db.models.fields.related.ForeignKey')(related_name='clone_set', null=True, to=orm['main.ConfigEntity'])), + ('selections', self.gf('footprint.main.models.config.model_pickled_object_field.SelectionModelsPickledObjectField')(default={'db_entities': {}, 'sets': {}})), + )) + db.send_create_signal('main', ['ConfigEntity']) + + # Adding M2M table for field categories on 'ConfigEntity' + db.create_table('main_configentity_categories', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('configentity', models.ForeignKey(orm['main.configentity'], null=False)), + ('category', models.ForeignKey(orm['main.category'], null=False)) + )) + db.create_unique('main_configentity_categories', ['configentity_id', 'category_id']) + + # Adding M2M table for field built_form_sets on 'ConfigEntity' + db.create_table('main_configentity_built_form_sets', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('configentity', models.ForeignKey(orm['main.configentity'], null=False)), + ('builtformset', models.ForeignKey(orm['main.builtformset'], null=False)) + )) + db.create_unique('main_configentity_built_form_sets', ['configentity_id', 'builtformset_id']) + + # Adding M2M table for field policy_sets on 'ConfigEntity' + db.create_table('main_configentity_policy_sets', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('configentity', models.ForeignKey(orm['main.configentity'], null=False)), + ('policyset', models.ForeignKey(orm['main.policyset'], null=False)) + )) + db.create_unique('main_configentity_policy_sets', ['configentity_id', 'policyset_id']) + + # Adding M2M table for field media on 'ConfigEntity' + db.create_table('main_configentity_media', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('configentity', models.ForeignKey(orm['main.configentity'], null=False)), + ('medium', models.ForeignKey(orm['main.medium'], null=False)) + )) + db.create_unique('main_configentity_media', ['configentity_id', 'medium_id']) + + # Adding model 'Job' + db.create_table('main_job', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('hashid', self.gf('django.db.models.fields.CharField')(unique=True, max_length=36)), + ('task_id', self.gf('django.db.models.fields.CharField')(max_length=36)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='jobs', to=orm['auth.User'])), + ('type', self.gf('django.db.models.fields.CharField')(max_length=32)), + ('status', self.gf('django.db.models.fields.TextField')(blank=True)), + ('created_on', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('ended_on', self.gf('django.db.models.fields.DateTimeField')(null=True)), + ('data', self.gf('django.db.models.fields.TextField')(null=True)), + )) + db.send_create_signal('main', ['Job']) + + # Adding model 'PresentationMedium' + db.create_table('main_presentationmedium', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('presentation', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Presentation'])), + ('medium', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Medium'])), + ('visible', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('visible_attributes', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('db_entity_key', self.gf('django.db.models.fields.CharField')(max_length=50)), + ('medium_context', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('configuration', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('rendered_medium', self.gf('picklefield.fields.PickledObjectField')(null=True)), + )) + db.send_create_signal('main', ['PresentationMedium']) + + # Adding M2M table for field tags on 'PresentationMedium' + db.create_table('main_presentationmedium_tags', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('presentationmedium', models.ForeignKey(orm['main.presentationmedium'], null=False)), + ('tag', models.ForeignKey(orm['main.tag'], null=False)) + )) + db.create_unique('main_presentationmedium_tags', ['presentationmedium_id', 'tag_id']) + + # Adding model 'Layer' + db.create_table('main_layer', ( + ('presentationmedium_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.PresentationMedium'], unique=True, primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + )) + db.send_create_signal('main', ['Layer']) + + # Adding model 'PrimaryComponent' + db.create_table('main_primarycomponent', ( + ('builtform_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.BuiltForm'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['PrimaryComponent']) + + # Adding model 'PlacetypeComponentCategory' + db.create_table('main_placetypecomponentcategory', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('contributes_to_net', self.gf('django.db.models.fields.BooleanField')(default=False)), + )) + db.send_create_signal('main', ['PlacetypeComponentCategory']) + + # Adding model 'PlacetypeComponent' + db.create_table('main_placetypecomponent', ( + ('builtform_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.BuiltForm'], unique=True, primary_key=True)), + ('component_category', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.PlacetypeComponentCategory'])), + )) + db.send_create_signal('main', ['PlacetypeComponent']) + + # Adding model 'StreetAttributeSet' + db.create_table('main_streetattributeset', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('lane_width', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=8, decimal_places=4)), + ('number_of_lanes', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=8, decimal_places=4)), + ('block_size', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=8, decimal_places=4)), + )) + db.send_create_signal('main', ['StreetAttributeSet']) + + # Adding model 'Placetype' + db.create_table('main_placetype', ( + ('builtform_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.BuiltForm'], unique=True, primary_key=True)), + ('street_attributes', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.StreetAttributeSet'], null=True)), + ('intersection_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=8, decimal_places=4)), + )) + db.send_create_signal('main', ['Placetype']) + + # Adding model 'FlatBuiltForm' + db.create_table('main_flatbuiltform', ( + ('built_form_id', self.gf('django.db.models.fields.IntegerField')(primary_key=True)), + ('key', self.gf('django.db.models.fields.CharField')(max_length=120)), + ('intersection_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('built_form_type', self.gf('django.db.models.fields.CharField')(max_length=50)), + ('gross_net_ratio', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=11, decimal_places=10)), + ('dwelling_unit_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('household_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('population_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('employment_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('single_family_large_lot_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('single_family_small_lot_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('attached_single_family_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('multifamily_2_to_4_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('multifamily_5_plus_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('retail_services_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('restaurant_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('arts_entertainment_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('accommodation_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('other_services_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('office_services_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('public_admin_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('education_services_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('medical_services_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('manufacturing_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('wholesale_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('transport_warehouse_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('construction_utilities_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('agriculture_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('extraction_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('armed_forces_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('office_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('retail_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('industrial_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('residential_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('agricultural_density', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_mixed_use', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_residential', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_employment', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_mixed_use_with_office', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_mixed_use_without_office', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_residential_single_family_small_lot', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_residential_single_family_large_lot', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_residential_attached_single_family', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_residential_multifamily', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_employment_office', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_employment_retail', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_employment_industrial', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_employment_agriculture', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('acres_parcel_employment_mixed', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=10)), + ('building_sqft_total', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_detached_single_family', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_single_family_small_lot', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_single_family_large_lot', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_attached_single_family', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_multifamily_2_to_4', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_multifamily_5_plus', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_retail_services', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_restaurant', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_accommodation', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_arts_entertainment', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_other_services', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_office_services', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_public_admin', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_education_services', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_medical_services', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_wholesale', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_transport_warehouse', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('building_sqft_industrial_non_warehouse', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('residential_irrigated_square_feet', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('commercial_irrigated_square_feet', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=15, decimal_places=7)), + ('softscape_and_landscape_percent', self.gf('django.db.models.fields.DecimalField')(null=True, max_digits=15, decimal_places=7)), + ('irrigated_percent', self.gf('django.db.models.fields.DecimalField')(default=0, null=True, max_digits=15, decimal_places=7)), + ('percent_streets', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=6, decimal_places=5)), + ('percent_parks', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=6, decimal_places=5)), + ('percent_civic', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=6, decimal_places=5)), + ('percent_mixed_use', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=6, decimal_places=5)), + ('percent_residential', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=6, decimal_places=5)), + ('percent_employment', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=6, decimal_places=5)), + ('pt_density', self.gf('django.db.models.fields.IntegerField')(null=True)), + ('pt_connectivity', self.gf('django.db.models.fields.IntegerField')(null=True)), + ('pt_land_use_mix', self.gf('django.db.models.fields.IntegerField')(null=True)), + ('pt_score', self.gf('django.db.models.fields.IntegerField')(null=True)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('intersections_sqmi', self.gf('django.db.models.fields.IntegerField')(null=True)), + ('avg_estimated_building_height_feet', self.gf('django.db.models.fields.IntegerField')(null=True)), + ('building_avg_number_of_floors', self.gf('django.db.models.fields.IntegerField')(null=True)), + ('block_avg_size_acres', self.gf('django.db.models.fields.IntegerField')(null=True)), + ('street_pattern', self.gf('django.db.models.fields.CharField')(max_length=100, null=True)), + )) + db.send_create_signal('main', ['FlatBuiltForm']) + + # Adding model 'Core' + db.create_table('main_core', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('config_entity', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.ConfigEntity'])), + ('celery_task', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('previous_celery_task', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('started', self.gf('django.db.models.fields.DateField')(null=True)), + ('completed', self.gf('django.db.models.fields.DateField')(null=True)), + ('failed', self.gf('django.db.models.fields.DateField')(null=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True)), + )) + db.send_create_signal('main', ['Core']) + + # Adding model 'Fiscal' + db.create_table('main_fiscal', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('config_entity', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.ConfigEntity'])), + ('celery_task', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('previous_celery_task', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('started', self.gf('django.db.models.fields.DateField')(null=True)), + ('completed', self.gf('django.db.models.fields.DateField')(null=True)), + ('failed', self.gf('django.db.models.fields.DateField')(null=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True)), + )) + db.send_create_signal('main', ['Fiscal']) + + # Adding model 'Vmt' + db.create_table('main_vmt', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('config_entity', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.ConfigEntity'])), + ('celery_task', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('previous_celery_task', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('started', self.gf('django.db.models.fields.DateField')(null=True)), + ('completed', self.gf('django.db.models.fields.DateField')(null=True)), + ('failed', self.gf('django.db.models.fields.DateField')(null=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True)), + )) + db.send_create_signal('main', ['Vmt']) + + # Adding model 'PrimaryComponentPercent' + db.create_table('main_primarycomponentpercent', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('percent', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=21, decimal_places=20)), + ('primary_component', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.PrimaryComponent'])), + ('placetype_component', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.PlacetypeComponent'])), + )) + db.send_create_signal('main', ['PrimaryComponentPercent']) + + # Adding model 'PlacetypeComponentPercent' + db.create_table('main_placetypecomponentpercent', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('percent', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=21, decimal_places=20)), + ('placetype_component', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.PlacetypeComponent'], null=True)), + ('placetype', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Placetype'])), + )) + db.send_create_signal('main', ['PlacetypeComponentPercent']) + + # Adding model 'Interest' + db.create_table('main_interest', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=120)), + )) + db.send_create_signal('main', ['Interest']) + + # Adding model 'DbEntityInterest' + db.create_table('main_dbentityinterest', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('config_entity', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.ConfigEntity'])), + ('db_entity', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.DbEntity'])), + ('interest', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Interest'])), + )) + db.send_create_signal('main', ['DbEntityInterest']) + + # Adding model 'GlobalConfig' + db.create_table('main_globalconfig', ( + ('configentity_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.ConfigEntity'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['GlobalConfig']) + + # Adding model 'Region' + db.create_table('main_region', ( + ('configentity_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.ConfigEntity'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['Region']) + + # Adding model 'Project' + db.create_table('main_project', ( + ('configentity_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.ConfigEntity'], unique=True, primary_key=True)), + ('base_year', self.gf('django.db.models.fields.IntegerField')(default=2005)), + )) + db.send_create_signal('main', ['Project']) + + # Adding model 'Scenario' + db.create_table('main_scenario', ( + ('configentity_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.ConfigEntity'], unique=True, primary_key=True)), + ('year', self.gf('django.db.models.fields.IntegerField')()), + )) + db.send_create_signal('main', ['Scenario']) + + # Adding model 'BaseScenario' + db.create_table('main_basescenario', ( + ('scenario_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Scenario'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['BaseScenario']) + + # Adding model 'FutureScenario' + db.create_table('main_futurescenario', ( + ('scenario_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Scenario'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['FutureScenario']) + + # Adding model 'Parcel' + db.create_table('main_parcel', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('wkb_geometry', self.gf('django.contrib.gis.db.models.fields.GeometryField')()), + )) + db.send_create_signal('main', ['Parcel']) + + # Adding model 'GridCell' + db.create_table('main_gridcell', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('geometry', self.gf('django.contrib.gis.db.models.fields.GeometryField')()), + ('source_table_id', self.gf('django.db.models.fields.IntegerField')(db_index=True)), + ('source_id', self.gf('django.db.models.fields.IntegerField')(max_length=200, db_index=True)), + )) + db.send_create_signal('main', ['GridCell']) + + # Adding model 'Taz' + db.create_table('main_taz', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('geometry', self.gf('django.contrib.gis.db.models.fields.GeometryField')()), + ('source_table_id', self.gf('django.db.models.fields.IntegerField')(db_index=True)), + ('source_id', self.gf('django.db.models.fields.IntegerField')(max_length=200, db_index=True)), + )) + db.send_create_signal('main', ['Taz']) + + # Adding model 'Result' + db.create_table('main_result', ( + ('presentationmedium_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.PresentationMedium'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['Result']) + + # Adding model 'Chart' + db.create_table('main_chart', ( + ('result_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Result'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['Chart']) + + # Adding model 'GeoLibrary' + db.create_table('main_geolibrary', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + )) + db.send_create_signal('main', ['GeoLibrary']) + + # Adding model 'GeoLibraryCatalog' + db.create_table('main_geolibrarycatalog', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('entity', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.DbEntity'])), + ('geo_library', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.GeoLibrary'])), + ('position', self.gf('django.db.models.fields.IntegerField')()), + )) + db.send_create_signal('main', ['GeoLibraryCatalog']) + + # Adding model 'Grid' + db.create_table('main_grid', ( + ('result_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Result'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['Grid']) + + # Adding model 'LayerChart' + db.create_table('main_layerchart', ( + ('chart_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Chart'], unique=True, primary_key=True)), + ('layer', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Layer'])), + )) + db.send_create_signal('main', ['LayerChart']) + + # Adding model 'Presentation' + db.create_table('main_presentation', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('key', self.gf('django.db.models.fields.CharField')(max_length=120)), + ('scope', self.gf('django.db.models.fields.CharField')(max_length=120)), + ('configuration', self.gf('picklefield.fields.PickledObjectField')(null=True)), + ('config_entity', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.ConfigEntity'])), + )) + db.send_create_signal('main', ['Presentation']) + + # Adding model 'LayerLibrary' + db.create_table('main_layerlibrary', ( + ('presentation_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Presentation'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['LayerLibrary']) + + # Adding model 'Map' + db.create_table('main_map', ( + ('presentation_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Presentation'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['Map']) + + # Adding model 'Painting' + db.create_table('main_painting', ( + ('map_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Map'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['Painting']) + + # Adding model 'Report' + db.create_table('main_report', ( + ('presentation_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Presentation'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['Report']) + + # Adding model 'ResultLibrary' + db.create_table('main_resultlibrary', ( + ('presentation_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Presentation'], unique=True, primary_key=True)), + )) + db.send_create_signal('main', ['ResultLibrary']) + + # Adding model 'Style' + db.create_table('main_style', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=120)), + ('identifier', self.gf('django.db.models.fields.TextField')()), + ('target', self.gf('django.db.models.fields.TextField')()), + ('style_property', self.gf('django.db.models.fields.TextField')()), + )) + db.send_create_signal('main', ['Style']) + + # Adding model 'Template' + db.create_table('main_template', ( + ('medium_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.Medium'], unique=True, primary_key=True)), + ('template_context', self.gf('picklefield.fields.PickledObjectField')()), + )) + db.send_create_signal('main', ['Template']) + + # Adding model 'PresentationConfiguration' + db.create_table('main_presentationconfiguration', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('key', self.gf('django.db.models.fields.CharField')(max_length=120)), + ('scope', self.gf('django.db.models.fields.CharField')(max_length=120)), + ('data', self.gf('picklefield.fields.PickledObjectField')()), + )) + db.send_create_signal('main', ['PresentationConfiguration']) + + # Adding model 'SortType' + db.create_table('main_sorttype', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('description', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=120)), + ('order_by', self.gf('django.db.models.fields.CharField')(default=None, max_length=100, unique=True, null=True)), + )) + db.send_create_signal('main', ['SortType']) + + # Adding model 'TileStacheConfig' + db.create_table('main_tilestacheconfig', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(default='default', max_length=50)), + ('config', self.gf('picklefield.fields.PickledObjectField')()), + ('enable_caching', self.gf('django.db.models.fields.NullBooleanField')(default=True, null=True, blank=True)), + )) + db.send_create_signal('main', ['TileStacheConfig']) + + # Adding model 'SacogLandUseDefinition' + db.create_table('main_sacoglandusedefinition', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('land_use', self.gf('django.db.models.fields.CharField')(max_length=100, null=True, blank=True)), + ('min_du_ac', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('max_du_ac', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('max_emp_ac', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('rural_flag', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('detached_flag', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('attached_flag', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('pct_ret_rest', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_ret_ret', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_ret_svc', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_off_gov', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_off_off', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_off_svc', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_off_med', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_ind', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_pub_edu', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_pub_med', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_pub_gov', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + ('pct_other', self.gf('django.db.models.fields.DecimalField')(default=0, max_digits=9, decimal_places=2)), + )) + db.send_create_signal('main', ['SacogLandUseDefinition']) + + # Adding model 'SacogLandUse' + db.create_table('main_sacoglanduse', ( + ('builtform_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.BuiltForm'], unique=True, primary_key=True)), + ('land_use_definition', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.SacogLandUseDefinition'])), + )) + db.send_create_signal('main', ['SacogLandUse']) + + # Adding model 'ScagLandUseDefinition' + db.create_table('main_scaglandusedefinition', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('land_use_description', self.gf('django.db.models.fields.CharField')(max_length=100, null=True, blank=True)), + ('land_use_type', self.gf('django.db.models.fields.CharField')(max_length=100, null=True, blank=True)), + ('land_use', self.gf('django.db.models.fields.IntegerField')()), + )) + db.send_create_signal('main', ['ScagLandUseDefinition']) + + # Adding model 'ScagLandUse' + db.create_table('main_scaglanduse', ( + ('builtform_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['main.BuiltForm'], unique=True, primary_key=True)), + ('land_use_definition', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.ScagLandUseDefinition'])), + )) + db.send_create_signal('main', ['ScagLandUse']) + + + def backwards(self, orm): + # Deleting model 'BuildingUsePercent' + db.delete_table('main_buildingusepercent') + + # Deleting model 'BuildingUseDefinition' + db.delete_table('main_buildingusedefinition') + + # Deleting model 'BuildingAttributeSet' + db.delete_table('main_buildingattributeset') + + # Deleting model 'Tag' + db.delete_table('main_tag') + + # Deleting model 'Medium' + db.delete_table('main_medium') + + # Deleting model 'BuiltFormExample' + db.delete_table('main_builtformexample') + + # Deleting model 'BuiltForm' + db.delete_table('main_builtform') + + # Removing M2M table for field tags on 'BuiltForm' + db.delete_table('main_builtform_tags') + + # Removing M2M table for field media on 'BuiltForm' + db.delete_table('main_builtform_media') + + # Removing M2M table for field examples on 'BuiltForm' + db.delete_table('main_builtform_examples') + + # Deleting model 'DbEntity' + db.delete_table('main_dbentity') + + # Removing M2M table for field tags on 'DbEntity' + db.delete_table('main_dbentity_tags') + + # Deleting model 'Category' + db.delete_table('main_category') + + # Deleting model 'BuiltFormSet' + db.delete_table('main_builtformset') + + # Removing M2M table for field built_forms on 'BuiltFormSet' + db.delete_table('main_builtformset_built_forms') + + # Deleting model 'Policy' + db.delete_table('main_policy') + + # Removing M2M table for field tags on 'Policy' + db.delete_table('main_policy_tags') + + # Removing M2M table for field policies on 'Policy' + db.delete_table('main_policy_policies') + + # Deleting model 'PolicySet' + db.delete_table('main_policyset') + + # Removing M2M table for field policies on 'PolicySet' + db.delete_table('main_policyset_policies') + + # Deleting model 'ConfigEntity' + db.delete_table('main_configentity') + + # Removing M2M table for field categories on 'ConfigEntity' + db.delete_table('main_configentity_categories') + + # Removing M2M table for field built_form_sets on 'ConfigEntity' + db.delete_table('main_configentity_built_form_sets') + + # Removing M2M table for field policy_sets on 'ConfigEntity' + db.delete_table('main_configentity_policy_sets') + + # Removing M2M table for field media on 'ConfigEntity' + db.delete_table('main_configentity_media') + + # Deleting model 'Job' + db.delete_table('main_job') + + # Deleting model 'PresentationMedium' + db.delete_table('main_presentationmedium') + + # Removing M2M table for field tags on 'PresentationMedium' + db.delete_table('main_presentationmedium_tags') + + # Deleting model 'Layer' + db.delete_table('main_layer') + + # Deleting model 'PrimaryComponent' + db.delete_table('main_primarycomponent') + + # Deleting model 'PlacetypeComponentCategory' + db.delete_table('main_placetypecomponentcategory') + + # Deleting model 'PlacetypeComponent' + db.delete_table('main_placetypecomponent') + + # Deleting model 'StreetAttributeSet' + db.delete_table('main_streetattributeset') + + # Deleting model 'Placetype' + db.delete_table('main_placetype') + + # Deleting model 'FlatBuiltForm' + db.delete_table('main_flatbuiltform') + + # Deleting model 'Core' + db.delete_table('main_core') + + # Deleting model 'Fiscal' + db.delete_table('main_fiscal') + + # Deleting model 'Vmt' + db.delete_table('main_vmt') + + # Deleting model 'PrimaryComponentPercent' + db.delete_table('main_primarycomponentpercent') + + # Deleting model 'PlacetypeComponentPercent' + db.delete_table('main_placetypecomponentpercent') + + # Deleting model 'Interest' + db.delete_table('main_interest') + + # Deleting model 'DbEntityInterest' + db.delete_table('main_dbentityinterest') + + # Deleting model 'GlobalConfig' + db.delete_table('main_globalconfig') + + # Deleting model 'Region' + db.delete_table('main_region') + + # Deleting model 'Project' + db.delete_table('main_project') + + # Deleting model 'Scenario' + db.delete_table('main_scenario') + + # Deleting model 'BaseScenario' + db.delete_table('main_basescenario') + + # Deleting model 'FutureScenario' + db.delete_table('main_futurescenario') + + # Deleting model 'Parcel' + db.delete_table('main_parcel') + + # Deleting model 'GridCell' + db.delete_table('main_gridcell') + + # Deleting model 'Taz' + db.delete_table('main_taz') + + # Deleting model 'Result' + db.delete_table('main_result') + + # Deleting model 'Chart' + db.delete_table('main_chart') + + # Deleting model 'GeoLibrary' + db.delete_table('main_geolibrary') + + # Deleting model 'GeoLibraryCatalog' + db.delete_table('main_geolibrarycatalog') + + # Deleting model 'Grid' + db.delete_table('main_grid') + + # Deleting model 'LayerChart' + db.delete_table('main_layerchart') + + # Deleting model 'Presentation' + db.delete_table('main_presentation') + + # Deleting model 'LayerLibrary' + db.delete_table('main_layerlibrary') + + # Deleting model 'Map' + db.delete_table('main_map') + + # Deleting model 'Painting' + db.delete_table('main_painting') + + # Deleting model 'Report' + db.delete_table('main_report') + + # Deleting model 'ResultLibrary' + db.delete_table('main_resultlibrary') + + # Deleting model 'Style' + db.delete_table('main_style') + + # Deleting model 'Template' + db.delete_table('main_template') + + # Deleting model 'PresentationConfiguration' + db.delete_table('main_presentationconfiguration') + + # Deleting model 'SortType' + db.delete_table('main_sorttype') + + # Deleting model 'TileStacheConfig' + db.delete_table('main_tilestacheconfig') + + # Deleting model 'SacogLandUseDefinition' + db.delete_table('main_sacoglandusedefinition') + + # Deleting model 'SacogLandUse' + db.delete_table('main_sacoglanduse') + + # Deleting model 'ScagLandUseDefinition' + db.delete_table('main_scaglandusedefinition') + + # Deleting model 'ScagLandUse' + db.delete_table('main_scaglanduse') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.basescenario': { + 'Meta': {'object_name': 'BaseScenario', '_ormbases': ['main.Scenario']}, + 'scenario_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Scenario']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.buildingattributeset': { + 'Meta': {'object_name': 'BuildingAttributeSet'}, + 'building_uses': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuildingUseDefinition']", 'through': "orm['main.BuildingUsePercent']", 'symmetrical': 'False'}), + 'combined_pop_emp_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '4'}), + 'commercial_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'floors': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '3'}), + 'gross_net_ratio': ('django.db.models.fields.DecimalField', [], {'default': '1', 'max_digits': '8', 'decimal_places': '7'}), + 'gross_population_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '14', 'decimal_places': '10'}), + 'hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'household_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '14', 'decimal_places': '10'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'impervious_hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'impervious_roof_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'irrigated_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'parking_spaces': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '7', 'decimal_places': '3'}), + 'parking_structure_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pervious_hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'residential_average_lot_size': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'residential_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'softscape_and_landscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'total_far': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '10', 'decimal_places': '7'}) + }, + 'main.buildingusedefinition': { + 'Meta': {'object_name': 'BuildingUseDefinition'}, + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingUseDefinition']", 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.buildingusepercent': { + 'Meta': {'object_name': 'BuildingUsePercent'}, + 'building_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingAttributeSet']"}), + 'building_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingUseDefinition']"}), + 'efficiency': ('django.db.models.fields.DecimalField', [], {'default': '0.85', 'max_digits': '6', 'decimal_places': '4'}), + 'floor_area_ratio': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '12', 'decimal_places': '10'}), + 'gross_built_up_area': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '13', 'decimal_places': '3'}), + 'household_size': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'net_built_up_area': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '13', 'decimal_places': '3'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'square_feet_per_unit': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '11', 'decimal_places': '3'}), + 'unit_density': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '16', 'decimal_places': '10'}), + 'vacancy_rate': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '4', 'decimal_places': '3'}) + }, + 'main.builtform': { + 'Meta': {'object_name': 'BuiltForm'}, + 'building_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingAttributeSet']", 'null': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'examples': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltFormExample']", 'null': 'True', 'symmetrical': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'media': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'built_form_media'", 'symmetrical': 'False', 'to': "orm['main.Medium']"}), + 'medium': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Medium']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_built_form': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuiltForm']", 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}) + }, + 'main.builtformexample': { + 'Meta': {'object_name': 'BuiltFormExample'}, + 'content': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'url_aerial': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'url_street': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'main.builtformset': { + 'Meta': {'object_name': 'BuiltFormSet'}, + 'built_forms': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltForm']", 'symmetrical': 'False'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.category': { + 'Meta': {'object_name': 'Category'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.chart': { + 'Meta': {'object_name': 'Chart', '_ormbases': ['main.Result']}, + 'result_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Result']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.configentity': { + 'Meta': {'object_name': 'ConfigEntity'}, + 'bounds': ('django.contrib.gis.db.models.fields.MultiPolygonField', [], {}), + 'built_form_sets': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltFormSet']", 'symmetrical': 'False'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Category']", 'symmetrical': 'False'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'config_entity_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'db_entities': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.DbEntity']", 'through': "orm['main.DbEntityInterest']", 'symmetrical': 'False'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'media': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Medium']", 'null': 'True', 'symmetrical': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_config_entity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clone_set'", 'null': 'True', 'to': "orm['main.ConfigEntity']"}), + 'parent_config_entity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parent_set'", 'null': 'True', 'to': "orm['main.ConfigEntity']"}), + 'policy_sets': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PolicySet']", 'symmetrical': 'False'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'selections': ('footprint.main.models.config.model_pickled_object_field.SelectionModelsPickledObjectField', [], {'default': "{'db_entities': {}, 'sets': {}}"}), + 'updater': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'config_entity_updater'", 'null': 'True', 'to': "orm['auth.User']"}) + }, + 'main.core': { + 'Meta': {'object_name': 'Core'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + }, + 'main.dbentity': { + 'Meta': {'object_name': 'DbEntity'}, + 'class_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'db_entity_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'extent_authority': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'feature_class_configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'group_by': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'hosts': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'query': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'schema': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'srid': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'table': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'updater': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'db_entity_updater'", 'null': 'True', 'to': "orm['auth.User']"}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True'}) + }, + 'main.dbentityinterest': { + 'Meta': {'object_name': 'DbEntityInterest'}, + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'db_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interest': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Interest']"}) + }, + 'main.fiscal': { + 'Meta': {'object_name': 'Fiscal'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + }, + 'main.flatbuiltform': { + 'Meta': {'object_name': 'FlatBuiltForm'}, + 'accommodation_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_agriculture': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_industrial': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_mixed': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_retail': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use_with_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use_without_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_attached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_multifamily': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_single_family_large_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_single_family_small_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'agricultural_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'agriculture_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'armed_forces_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'arts_entertainment_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'attached_single_family_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'avg_estimated_building_height_feet': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'block_avg_size_acres': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'building_avg_number_of_floors': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'building_sqft_accommodation': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_arts_entertainment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_attached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_detached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_education_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_industrial_non_warehouse': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_medical_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_multifamily_2_to_4': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_multifamily_5_plus': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_office_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_other_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_public_admin': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_restaurant': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_retail_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_single_family_large_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_single_family_small_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_total': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_transport_warehouse': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_wholesale': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'built_form_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'built_form_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'commercial_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'construction_utilities_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'dwelling_unit_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'education_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'employment_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'extraction_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'gross_net_ratio': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '11', 'decimal_places': '10'}), + 'household_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'industrial_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'intersection_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'intersections_sqmi': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'irrigated_percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '15', 'decimal_places': '7'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'manufacturing_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'medical_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'multifamily_2_to_4_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'multifamily_5_plus_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'office_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'office_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'other_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'percent_civic': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_employment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_mixed_use': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_parks': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_residential': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_streets': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'population_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'pt_connectivity': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_density': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_land_use_mix': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_score': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'public_admin_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'residential_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'residential_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'restaurant_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'retail_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'retail_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'single_family_large_lot_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'single_family_small_lot_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'softscape_and_landscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '15', 'decimal_places': '7'}), + 'street_pattern': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'transport_warehouse_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'wholesale_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}) + }, + 'main.futurescenario': { + 'Meta': {'object_name': 'FutureScenario', '_ormbases': ['main.Scenario']}, + 'scenario_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Scenario']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.geolibrary': { + 'Meta': {'object_name': 'GeoLibrary'}, + 'entities': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.DbEntity']", 'through': "orm['main.GeoLibraryCatalog']", 'symmetrical': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'main.geolibrarycatalog': { + 'Meta': {'object_name': 'GeoLibraryCatalog'}, + 'entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']"}), + 'geo_library': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.GeoLibrary']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'position': ('django.db.models.fields.IntegerField', [], {}) + }, + 'main.globalconfig': { + 'Meta': {'object_name': 'GlobalConfig', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.grid': { + 'Meta': {'object_name': 'Grid', '_ormbases': ['main.Result']}, + 'result_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Result']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.gridcell': { + 'Meta': {'object_name': 'GridCell'}, + 'geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'source_id': ('django.db.models.fields.IntegerField', [], {'max_length': '200', 'db_index': 'True'}), + 'source_table_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'main.interest': { + 'Meta': {'object_name': 'Interest'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}) + }, + 'main.job': { + 'Meta': {'ordering': "['-created_on']", 'object_name': 'Job'}, + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'data': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'ended_on': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'hashid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'task_id': ('django.db.models.fields.CharField', [], {'max_length': '36'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'to': "orm['auth.User']"}) + }, + 'main.layer': { + 'Meta': {'object_name': 'Layer', '_ormbases': ['main.PresentationMedium']}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'presentationmedium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.PresentationMedium']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.layerchart': { + 'Meta': {'object_name': 'LayerChart', '_ormbases': ['main.Chart']}, + 'chart_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Chart']", 'unique': 'True', 'primary_key': 'True'}), + 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Layer']"}) + }, + 'main.layerlibrary': { + 'Meta': {'object_name': 'LayerLibrary', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.map': { + 'Meta': {'object_name': 'Map', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.medium': { + 'Meta': {'object_name': 'Medium'}, + 'content': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'content_type': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'main.painting': { + 'Meta': {'object_name': 'Painting', '_ormbases': ['main.Map']}, + 'map_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Map']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.parcel': { + 'Meta': {'object_name': 'Parcel'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'wkb_geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}) + }, + 'main.placetype': { + 'Meta': {'object_name': 'Placetype', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'intersection_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'placetype_components': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PlacetypeComponent']", 'through': "orm['main.PlacetypeComponentPercent']", 'symmetrical': 'False'}), + 'street_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.StreetAttributeSet']", 'null': 'True'}) + }, + 'main.placetypecomponent': { + 'Meta': {'object_name': 'PlacetypeComponent', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'component_category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponentCategory']"}), + 'primary_components': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PrimaryComponent']", 'through': "orm['main.PrimaryComponentPercent']", 'symmetrical': 'False'}) + }, + 'main.placetypecomponentcategory': { + 'Meta': {'object_name': 'PlacetypeComponentCategory'}, + 'contributes_to_net': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.placetypecomponentpercent': { + 'Meta': {'object_name': 'PlacetypeComponentPercent'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'placetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Placetype']"}), + 'placetype_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponent']", 'null': 'True'}) + }, + 'main.policy': { + 'Meta': {'object_name': 'Policy'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'policies': ('django.db.models.fields.related.ManyToManyField', [], {'default': '[]', 'to': "orm['main.Policy']", 'symmetrical': 'False'}), + 'schema': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'values': ('picklefield.fields.PickledObjectField', [], {}) + }, + 'main.policyset': { + 'Meta': {'object_name': 'PolicySet'}, + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'policies': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Policy']", 'symmetrical': 'False'}) + }, + 'main.presentation': { + 'Meta': {'object_name': 'Presentation'}, + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}) + }, + 'main.presentationconfiguration': { + 'Meta': {'object_name': 'PresentationConfiguration'}, + 'data': ('picklefield.fields.PickledObjectField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}) + }, + 'main.presentationmedium': { + 'Meta': {'object_name': 'PresentationMedium'}, + 'configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'db_entity_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'medium': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Medium']"}), + 'medium_context': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'presentation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Presentation']"}), + 'rendered_medium': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'visible_attributes': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}) + }, + 'main.primarycomponent': { + 'Meta': {'object_name': 'PrimaryComponent', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.primarycomponentpercent': { + 'Meta': {'object_name': 'PrimaryComponentPercent'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'placetype_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponent']"}), + 'primary_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PrimaryComponent']"}) + }, + 'main.project': { + 'Meta': {'object_name': 'Project', '_ormbases': ['main.ConfigEntity']}, + 'base_year': ('django.db.models.fields.IntegerField', [], {'default': '2005'}), + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.region': { + 'Meta': {'object_name': 'Region', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.report': { + 'Meta': {'object_name': 'Report', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.result': { + 'Meta': {'object_name': 'Result', '_ormbases': ['main.PresentationMedium']}, + 'presentationmedium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.PresentationMedium']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.resultlibrary': { + 'Meta': {'object_name': 'ResultLibrary', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.sacoglanduse': { + 'Meta': {'object_name': 'SacogLandUse'}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'land_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.SacogLandUseDefinition']"}) + }, + 'main.sacoglandusedefinition': { + 'Meta': {'object_name': 'SacogLandUseDefinition'}, + 'attached_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'detached_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'land_use': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_du_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'max_emp_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'min_du_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ind': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_gov': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_med': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_off': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_svc': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_other': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_edu': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_gov': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_med': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_rest': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_ret': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_svc': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'rural_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'main.scaglanduse': { + 'Meta': {'object_name': 'ScagLandUse'}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'land_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ScagLandUseDefinition']"}) + }, + 'main.scaglandusedefinition': { + 'Meta': {'object_name': 'ScagLandUseDefinition'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'land_use': ('django.db.models.fields.IntegerField', [], {}), + 'land_use_description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'land_use_type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}) + }, + 'main.scenario': { + 'Meta': {'object_name': 'Scenario', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}), + 'year': ('django.db.models.fields.IntegerField', [], {}) + }, + 'main.sorttype': { + 'Meta': {'object_name': 'SortType'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'order_by': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True', 'null': 'True'}) + }, + 'main.streetattributeset': { + 'Meta': {'object_name': 'StreetAttributeSet'}, + 'block_size': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lane_width': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'number_of_lanes': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}) + }, + 'main.style': { + 'Meta': {'object_name': 'Style'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'identifier': ('django.db.models.fields.TextField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'style_property': ('django.db.models.fields.TextField', [], {}), + 'target': ('django.db.models.fields.TextField', [], {}) + }, + 'main.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.taz': { + 'Meta': {'object_name': 'Taz'}, + 'geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'source_id': ('django.db.models.fields.IntegerField', [], {'max_length': '200', 'db_index': 'True'}), + 'source_table_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'main.template': { + 'Meta': {'object_name': 'Template', '_ormbases': ['main.Medium']}, + 'medium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Medium']", 'unique': 'True', 'primary_key': 'True'}), + 'template_context': ('picklefield.fields.PickledObjectField', [], {}) + }, + 'main.tilestacheconfig': { + 'Meta': {'object_name': 'TileStacheConfig'}, + 'config': ('picklefield.fields.PickledObjectField', [], {}), + 'enable_caching': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '50'}) + }, + 'main.vmt': { + 'Meta': {'object_name': 'Vmt'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + } + } + + complete_apps = ['main'] \ No newline at end of file diff --git a/footprint/main/migrations/0002_auto__add_field_layer_origin_instance.py b/footprint/main/migrations/0002_auto__add_field_layer_origin_instance.py new file mode 100644 index 000000000..d7f0b01d8 --- /dev/null +++ b/footprint/main/migrations/0002_auto__add_field_layer_origin_instance.py @@ -0,0 +1,621 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Layer.origin_instance' + db.add_column('main_layer', 'origin_instance', + self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Layer'], null=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Layer.origin_instance' + db.delete_column('main_layer', 'origin_instance_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.basescenario': { + 'Meta': {'object_name': 'BaseScenario', '_ormbases': ['main.Scenario']}, + 'scenario_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Scenario']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.buildingattributeset': { + 'Meta': {'object_name': 'BuildingAttributeSet'}, + 'building_uses': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuildingUseDefinition']", 'through': "orm['main.BuildingUsePercent']", 'symmetrical': 'False'}), + 'combined_pop_emp_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '4'}), + 'commercial_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'floors': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '3'}), + 'gross_net_ratio': ('django.db.models.fields.DecimalField', [], {'default': '1', 'max_digits': '8', 'decimal_places': '7'}), + 'gross_population_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '14', 'decimal_places': '10'}), + 'hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'household_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '14', 'decimal_places': '10'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'impervious_hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'impervious_roof_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'irrigated_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'parking_spaces': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '7', 'decimal_places': '3'}), + 'parking_structure_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pervious_hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'residential_average_lot_size': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'residential_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'softscape_and_landscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'total_far': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '10', 'decimal_places': '7'}) + }, + 'main.buildingusedefinition': { + 'Meta': {'object_name': 'BuildingUseDefinition'}, + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingUseDefinition']", 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.buildingusepercent': { + 'Meta': {'object_name': 'BuildingUsePercent'}, + 'building_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingAttributeSet']"}), + 'building_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingUseDefinition']"}), + 'efficiency': ('django.db.models.fields.DecimalField', [], {'default': '0.85', 'max_digits': '6', 'decimal_places': '4'}), + 'floor_area_ratio': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '12', 'decimal_places': '10'}), + 'gross_built_up_area': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '13', 'decimal_places': '3'}), + 'household_size': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'net_built_up_area': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '13', 'decimal_places': '3'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'square_feet_per_unit': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '11', 'decimal_places': '3'}), + 'unit_density': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '16', 'decimal_places': '10'}), + 'vacancy_rate': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '4', 'decimal_places': '3'}) + }, + 'main.builtform': { + 'Meta': {'object_name': 'BuiltForm'}, + 'building_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingAttributeSet']", 'null': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'examples': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltFormExample']", 'null': 'True', 'symmetrical': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'media': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'built_form_media'", 'symmetrical': 'False', 'to': "orm['main.Medium']"}), + 'medium': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Medium']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_built_form': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuiltForm']", 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}) + }, + 'main.builtformexample': { + 'Meta': {'object_name': 'BuiltFormExample'}, + 'content': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'url_aerial': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'url_street': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'main.builtformset': { + 'Meta': {'object_name': 'BuiltFormSet'}, + 'built_forms': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltForm']", 'symmetrical': 'False'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.category': { + 'Meta': {'object_name': 'Category'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.chart': { + 'Meta': {'object_name': 'Chart', '_ormbases': ['main.Result']}, + 'result_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Result']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.configentity': { + 'Meta': {'object_name': 'ConfigEntity'}, + 'bounds': ('django.contrib.gis.db.models.fields.MultiPolygonField', [], {}), + 'built_form_sets': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltFormSet']", 'symmetrical': 'False'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Category']", 'symmetrical': 'False'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'config_entity_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'db_entities': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.DbEntity']", 'through': "orm['main.DbEntityInterest']", 'symmetrical': 'False'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'media': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Medium']", 'null': 'True', 'symmetrical': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_config_entity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clone_set'", 'null': 'True', 'to': "orm['main.ConfigEntity']"}), + 'parent_config_entity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parent_set'", 'null': 'True', 'to': "orm['main.ConfigEntity']"}), + 'policy_sets': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PolicySet']", 'symmetrical': 'False'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'selections': ('footprint.main.models.config.model_pickled_object_field.SelectionModelsPickledObjectField', [], {'default': "{'db_entities': {}, 'sets': {}}"}), + 'updater': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'config_entity_updater'", 'null': 'True', 'to': "orm['auth.User']"}) + }, + 'main.core': { + 'Meta': {'object_name': 'Core'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + }, + 'main.dbentity': { + 'Meta': {'object_name': 'DbEntity'}, + 'class_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'db_entity_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'extent_authority': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'feature_class_configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'group_by': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'hosts': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'query': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'schema': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'srid': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'table': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'updater': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'db_entity_updater'", 'null': 'True', 'to': "orm['auth.User']"}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True'}) + }, + 'main.dbentityinterest': { + 'Meta': {'object_name': 'DbEntityInterest'}, + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'db_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interest': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Interest']"}) + }, + 'main.fiscal': { + 'Meta': {'object_name': 'Fiscal'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + }, + 'main.flatbuiltform': { + 'Meta': {'object_name': 'FlatBuiltForm'}, + 'accommodation_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_agriculture': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_industrial': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_mixed': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_retail': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use_with_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use_without_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_attached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_multifamily': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_single_family_large_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_single_family_small_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'agricultural_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'agriculture_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'armed_forces_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'arts_entertainment_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'attached_single_family_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'avg_estimated_building_height_feet': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'block_avg_size_acres': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'building_avg_number_of_floors': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'building_sqft_accommodation': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_arts_entertainment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_attached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_detached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_education_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_industrial_non_warehouse': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_medical_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_multifamily_2_to_4': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_multifamily_5_plus': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_office_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_other_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_public_admin': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_restaurant': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_retail_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_single_family_large_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_single_family_small_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_total': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_transport_warehouse': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_wholesale': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'built_form_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'built_form_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'commercial_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'construction_utilities_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'dwelling_unit_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'education_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'employment_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'extraction_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'gross_net_ratio': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '11', 'decimal_places': '10'}), + 'household_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'industrial_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'intersection_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'intersections_sqmi': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'irrigated_percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '15', 'decimal_places': '7'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'manufacturing_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'medical_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'multifamily_2_to_4_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'multifamily_5_plus_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'office_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'office_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'other_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'percent_civic': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_employment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_mixed_use': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_parks': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_residential': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_streets': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'population_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'pt_connectivity': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_density': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_land_use_mix': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_score': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'public_admin_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'residential_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'residential_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'restaurant_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'retail_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'retail_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'single_family_large_lot_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'single_family_small_lot_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'softscape_and_landscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '15', 'decimal_places': '7'}), + 'street_pattern': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'transport_warehouse_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'wholesale_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}) + }, + 'main.futurescenario': { + 'Meta': {'object_name': 'FutureScenario', '_ormbases': ['main.Scenario']}, + 'scenario_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Scenario']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.geolibrary': { + 'Meta': {'object_name': 'GeoLibrary'}, + 'entities': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.DbEntity']", 'through': "orm['main.GeoLibraryCatalog']", 'symmetrical': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'main.geolibrarycatalog': { + 'Meta': {'object_name': 'GeoLibraryCatalog'}, + 'entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']"}), + 'geo_library': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.GeoLibrary']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'position': ('django.db.models.fields.IntegerField', [], {}) + }, + 'main.globalconfig': { + 'Meta': {'object_name': 'GlobalConfig', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.grid': { + 'Meta': {'object_name': 'Grid', '_ormbases': ['main.Result']}, + 'result_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Result']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.gridcell': { + 'Meta': {'object_name': 'GridCell'}, + 'geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'source_id': ('django.db.models.fields.IntegerField', [], {'max_length': '200', 'db_index': 'True'}), + 'source_table_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'main.interest': { + 'Meta': {'object_name': 'Interest'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}) + }, + 'main.job': { + 'Meta': {'ordering': "['-created_on']", 'object_name': 'Job'}, + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'data': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'ended_on': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'hashid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'task_id': ('django.db.models.fields.CharField', [], {'max_length': '36'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'to': "orm['auth.User']"}) + }, + 'main.layer': { + 'Meta': {'object_name': 'Layer', '_ormbases': ['main.PresentationMedium']}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Layer']", 'null': 'True'}), + 'presentationmedium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.PresentationMedium']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.layerchart': { + 'Meta': {'object_name': 'LayerChart', '_ormbases': ['main.Chart']}, + 'chart_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Chart']", 'unique': 'True', 'primary_key': 'True'}), + 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Layer']"}) + }, + 'main.layerlibrary': { + 'Meta': {'object_name': 'LayerLibrary', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.map': { + 'Meta': {'object_name': 'Map', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.medium': { + 'Meta': {'object_name': 'Medium'}, + 'content': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'content_type': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'main.painting': { + 'Meta': {'object_name': 'Painting', '_ormbases': ['main.Map']}, + 'map_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Map']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.parcel': { + 'Meta': {'object_name': 'Parcel'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'wkb_geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}) + }, + 'main.placetype': { + 'Meta': {'object_name': 'Placetype', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'intersection_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'placetype_components': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PlacetypeComponent']", 'through': "orm['main.PlacetypeComponentPercent']", 'symmetrical': 'False'}), + 'street_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.StreetAttributeSet']", 'null': 'True'}) + }, + 'main.placetypecomponent': { + 'Meta': {'object_name': 'PlacetypeComponent', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'component_category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponentCategory']"}), + 'primary_components': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PrimaryComponent']", 'through': "orm['main.PrimaryComponentPercent']", 'symmetrical': 'False'}) + }, + 'main.placetypecomponentcategory': { + 'Meta': {'object_name': 'PlacetypeComponentCategory'}, + 'contributes_to_net': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.placetypecomponentpercent': { + 'Meta': {'object_name': 'PlacetypeComponentPercent'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'placetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Placetype']"}), + 'placetype_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponent']", 'null': 'True'}) + }, + 'main.policy': { + 'Meta': {'object_name': 'Policy'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'policies': ('django.db.models.fields.related.ManyToManyField', [], {'default': '[]', 'to': "orm['main.Policy']", 'symmetrical': 'False'}), + 'schema': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'values': ('picklefield.fields.PickledObjectField', [], {}) + }, + 'main.policyset': { + 'Meta': {'object_name': 'PolicySet'}, + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'policies': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Policy']", 'symmetrical': 'False'}) + }, + 'main.presentation': { + 'Meta': {'object_name': 'Presentation'}, + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}) + }, + 'main.presentationconfiguration': { + 'Meta': {'object_name': 'PresentationConfiguration'}, + 'data': ('picklefield.fields.PickledObjectField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}) + }, + 'main.presentationmedium': { + 'Meta': {'object_name': 'PresentationMedium'}, + 'configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'db_entity_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'medium': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Medium']"}), + 'medium_context': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'presentation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Presentation']"}), + 'rendered_medium': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'visible_attributes': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}) + }, + 'main.primarycomponent': { + 'Meta': {'object_name': 'PrimaryComponent', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.primarycomponentpercent': { + 'Meta': {'object_name': 'PrimaryComponentPercent'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'placetype_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponent']"}), + 'primary_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PrimaryComponent']"}) + }, + 'main.project': { + 'Meta': {'object_name': 'Project', '_ormbases': ['main.ConfigEntity']}, + 'base_year': ('django.db.models.fields.IntegerField', [], {'default': '2005'}), + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.region': { + 'Meta': {'object_name': 'Region', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.report': { + 'Meta': {'object_name': 'Report', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.result': { + 'Meta': {'object_name': 'Result', '_ormbases': ['main.PresentationMedium']}, + 'presentationmedium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.PresentationMedium']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.resultlibrary': { + 'Meta': {'object_name': 'ResultLibrary', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.sacoglanduse': { + 'Meta': {'object_name': 'SacogLandUse'}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'land_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.SacogLandUseDefinition']"}) + }, + 'main.sacoglandusedefinition': { + 'Meta': {'object_name': 'SacogLandUseDefinition'}, + 'attached_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'detached_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'land_use': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_du_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'max_emp_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'min_du_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ind': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_gov': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_med': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_off': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_svc': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_other': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_edu': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_gov': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_med': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_rest': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_ret': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_svc': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'rural_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'main.scaglanduse': { + 'Meta': {'object_name': 'ScagLandUse'}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'land_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ScagLandUseDefinition']"}) + }, + 'main.scaglandusedefinition': { + 'Meta': {'object_name': 'ScagLandUseDefinition'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'land_use': ('django.db.models.fields.IntegerField', [], {}), + 'land_use_description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'land_use_type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}) + }, + 'main.scenario': { + 'Meta': {'object_name': 'Scenario', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}), + 'year': ('django.db.models.fields.IntegerField', [], {}) + }, + 'main.sorttype': { + 'Meta': {'object_name': 'SortType'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'order_by': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True', 'null': 'True'}) + }, + 'main.streetattributeset': { + 'Meta': {'object_name': 'StreetAttributeSet'}, + 'block_size': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lane_width': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'number_of_lanes': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}) + }, + 'main.style': { + 'Meta': {'object_name': 'Style'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'identifier': ('django.db.models.fields.TextField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'style_property': ('django.db.models.fields.TextField', [], {}), + 'target': ('django.db.models.fields.TextField', [], {}) + }, + 'main.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.taz': { + 'Meta': {'object_name': 'Taz'}, + 'geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'source_id': ('django.db.models.fields.IntegerField', [], {'max_length': '200', 'db_index': 'True'}), + 'source_table_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'main.template': { + 'Meta': {'object_name': 'Template', '_ormbases': ['main.Medium']}, + 'medium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Medium']", 'unique': 'True', 'primary_key': 'True'}), + 'template_context': ('picklefield.fields.PickledObjectField', [], {}) + }, + 'main.tilestacheconfig': { + 'Meta': {'object_name': 'TileStacheConfig'}, + 'config': ('picklefield.fields.PickledObjectField', [], {}), + 'enable_caching': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '50'}) + }, + 'main.vmt': { + 'Meta': {'object_name': 'Vmt'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + } + } + + complete_apps = ['main'] \ No newline at end of file diff --git a/footprint/main/migrations/0003_auto__add_field_dbentity_origin_instance.py b/footprint/main/migrations/0003_auto__add_field_dbentity_origin_instance.py new file mode 100644 index 000000000..66d5b1472 --- /dev/null +++ b/footprint/main/migrations/0003_auto__add_field_dbentity_origin_instance.py @@ -0,0 +1,622 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'DbEntity.origin_instance' + db.add_column('main_dbentity', 'origin_instance', + self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.DbEntity'], null=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'DbEntity.origin_instance' + db.delete_column('main_dbentity', 'origin_instance_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.basescenario': { + 'Meta': {'object_name': 'BaseScenario', '_ormbases': ['main.Scenario']}, + 'scenario_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Scenario']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.buildingattributeset': { + 'Meta': {'object_name': 'BuildingAttributeSet'}, + 'building_uses': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuildingUseDefinition']", 'through': "orm['main.BuildingUsePercent']", 'symmetrical': 'False'}), + 'combined_pop_emp_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '4'}), + 'commercial_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'floors': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '3'}), + 'gross_net_ratio': ('django.db.models.fields.DecimalField', [], {'default': '1', 'max_digits': '8', 'decimal_places': '7'}), + 'gross_population_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '14', 'decimal_places': '10'}), + 'hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'household_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '14', 'decimal_places': '10'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'impervious_hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'impervious_roof_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'irrigated_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'parking_spaces': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '7', 'decimal_places': '3'}), + 'parking_structure_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pervious_hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'residential_average_lot_size': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'residential_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'softscape_and_landscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'total_far': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '10', 'decimal_places': '7'}) + }, + 'main.buildingusedefinition': { + 'Meta': {'object_name': 'BuildingUseDefinition'}, + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingUseDefinition']", 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.buildingusepercent': { + 'Meta': {'object_name': 'BuildingUsePercent'}, + 'building_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingAttributeSet']"}), + 'building_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingUseDefinition']"}), + 'efficiency': ('django.db.models.fields.DecimalField', [], {'default': '0.85', 'max_digits': '6', 'decimal_places': '4'}), + 'floor_area_ratio': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '12', 'decimal_places': '10'}), + 'gross_built_up_area': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '13', 'decimal_places': '3'}), + 'household_size': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'net_built_up_area': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '13', 'decimal_places': '3'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'square_feet_per_unit': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '11', 'decimal_places': '3'}), + 'unit_density': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '16', 'decimal_places': '10'}), + 'vacancy_rate': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '4', 'decimal_places': '3'}) + }, + 'main.builtform': { + 'Meta': {'object_name': 'BuiltForm'}, + 'building_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingAttributeSet']", 'null': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'examples': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltFormExample']", 'null': 'True', 'symmetrical': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'media': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'built_form_media'", 'symmetrical': 'False', 'to': "orm['main.Medium']"}), + 'medium': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Medium']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_built_form': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuiltForm']", 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}) + }, + 'main.builtformexample': { + 'Meta': {'object_name': 'BuiltFormExample'}, + 'content': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'url_aerial': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'url_street': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'main.builtformset': { + 'Meta': {'object_name': 'BuiltFormSet'}, + 'built_forms': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltForm']", 'symmetrical': 'False'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.category': { + 'Meta': {'object_name': 'Category'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.chart': { + 'Meta': {'object_name': 'Chart', '_ormbases': ['main.Result']}, + 'result_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Result']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.configentity': { + 'Meta': {'object_name': 'ConfigEntity'}, + 'bounds': ('django.contrib.gis.db.models.fields.MultiPolygonField', [], {}), + 'built_form_sets': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltFormSet']", 'symmetrical': 'False'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Category']", 'symmetrical': 'False'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'config_entity_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'db_entities': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.DbEntity']", 'through': "orm['main.DbEntityInterest']", 'symmetrical': 'False'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'media': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Medium']", 'null': 'True', 'symmetrical': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_config_entity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clone_set'", 'null': 'True', 'to': "orm['main.ConfigEntity']"}), + 'parent_config_entity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parent_set'", 'null': 'True', 'to': "orm['main.ConfigEntity']"}), + 'policy_sets': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PolicySet']", 'symmetrical': 'False'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'selections': ('footprint.main.models.config.model_pickled_object_field.SelectionModelsPickledObjectField', [], {'default': "{'db_entities': {}, 'sets': {}}"}), + 'updater': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'config_entity_updater'", 'null': 'True', 'to': "orm['auth.User']"}) + }, + 'main.core': { + 'Meta': {'object_name': 'Core'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + }, + 'main.dbentity': { + 'Meta': {'object_name': 'DbEntity'}, + 'class_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'db_entity_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'extent_authority': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'feature_class_configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'group_by': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'hosts': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']", 'null': 'True'}), + 'query': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'schema': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'srid': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'table': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'updater': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'db_entity_updater'", 'null': 'True', 'to': "orm['auth.User']"}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True'}) + }, + 'main.dbentityinterest': { + 'Meta': {'object_name': 'DbEntityInterest'}, + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'db_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interest': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Interest']"}) + }, + 'main.fiscal': { + 'Meta': {'object_name': 'Fiscal'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + }, + 'main.flatbuiltform': { + 'Meta': {'object_name': 'FlatBuiltForm'}, + 'accommodation_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_agriculture': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_industrial': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_mixed': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_retail': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use_with_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use_without_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_attached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_multifamily': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_single_family_large_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_single_family_small_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'agricultural_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'agriculture_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'armed_forces_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'arts_entertainment_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'attached_single_family_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'avg_estimated_building_height_feet': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'block_avg_size_acres': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'building_avg_number_of_floors': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'building_sqft_accommodation': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_arts_entertainment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_attached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_detached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_education_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_industrial_non_warehouse': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_medical_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_multifamily_2_to_4': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_multifamily_5_plus': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_office_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_other_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_public_admin': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_restaurant': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_retail_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_single_family_large_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_single_family_small_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_total': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_transport_warehouse': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_wholesale': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'built_form_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'built_form_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'commercial_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'construction_utilities_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'dwelling_unit_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'education_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'employment_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'extraction_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'gross_net_ratio': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '11', 'decimal_places': '10'}), + 'household_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'industrial_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'intersection_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'intersections_sqmi': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'irrigated_percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '15', 'decimal_places': '7'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'manufacturing_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'medical_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'multifamily_2_to_4_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'multifamily_5_plus_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'office_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'office_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'other_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'percent_civic': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_employment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_mixed_use': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_parks': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_residential': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_streets': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'population_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'pt_connectivity': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_density': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_land_use_mix': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_score': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'public_admin_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'residential_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'residential_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'restaurant_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'retail_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'retail_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'single_family_large_lot_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'single_family_small_lot_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'softscape_and_landscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '15', 'decimal_places': '7'}), + 'street_pattern': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'transport_warehouse_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'wholesale_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}) + }, + 'main.futurescenario': { + 'Meta': {'object_name': 'FutureScenario', '_ormbases': ['main.Scenario']}, + 'scenario_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Scenario']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.geolibrary': { + 'Meta': {'object_name': 'GeoLibrary'}, + 'entities': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.DbEntity']", 'through': "orm['main.GeoLibraryCatalog']", 'symmetrical': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'main.geolibrarycatalog': { + 'Meta': {'object_name': 'GeoLibraryCatalog'}, + 'entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']"}), + 'geo_library': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.GeoLibrary']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'position': ('django.db.models.fields.IntegerField', [], {}) + }, + 'main.globalconfig': { + 'Meta': {'object_name': 'GlobalConfig', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.grid': { + 'Meta': {'object_name': 'Grid', '_ormbases': ['main.Result']}, + 'result_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Result']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.gridcell': { + 'Meta': {'object_name': 'GridCell'}, + 'geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'source_id': ('django.db.models.fields.IntegerField', [], {'max_length': '200', 'db_index': 'True'}), + 'source_table_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'main.interest': { + 'Meta': {'object_name': 'Interest'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}) + }, + 'main.job': { + 'Meta': {'ordering': "['-created_on']", 'object_name': 'Job'}, + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'data': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'ended_on': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'hashid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'task_id': ('django.db.models.fields.CharField', [], {'max_length': '36'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'to': "orm['auth.User']"}) + }, + 'main.layer': { + 'Meta': {'object_name': 'Layer', '_ormbases': ['main.PresentationMedium']}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Layer']", 'null': 'True'}), + 'presentationmedium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.PresentationMedium']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.layerchart': { + 'Meta': {'object_name': 'LayerChart', '_ormbases': ['main.Chart']}, + 'chart_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Chart']", 'unique': 'True', 'primary_key': 'True'}), + 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Layer']"}) + }, + 'main.layerlibrary': { + 'Meta': {'object_name': 'LayerLibrary', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.map': { + 'Meta': {'object_name': 'Map', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.medium': { + 'Meta': {'object_name': 'Medium'}, + 'content': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'content_type': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'main.painting': { + 'Meta': {'object_name': 'Painting', '_ormbases': ['main.Map']}, + 'map_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Map']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.parcel': { + 'Meta': {'object_name': 'Parcel'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'wkb_geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}) + }, + 'main.placetype': { + 'Meta': {'object_name': 'Placetype', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'intersection_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'placetype_components': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PlacetypeComponent']", 'through': "orm['main.PlacetypeComponentPercent']", 'symmetrical': 'False'}), + 'street_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.StreetAttributeSet']", 'null': 'True'}) + }, + 'main.placetypecomponent': { + 'Meta': {'object_name': 'PlacetypeComponent', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'component_category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponentCategory']"}), + 'primary_components': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PrimaryComponent']", 'through': "orm['main.PrimaryComponentPercent']", 'symmetrical': 'False'}) + }, + 'main.placetypecomponentcategory': { + 'Meta': {'object_name': 'PlacetypeComponentCategory'}, + 'contributes_to_net': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.placetypecomponentpercent': { + 'Meta': {'object_name': 'PlacetypeComponentPercent'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'placetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Placetype']"}), + 'placetype_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponent']", 'null': 'True'}) + }, + 'main.policy': { + 'Meta': {'object_name': 'Policy'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'policies': ('django.db.models.fields.related.ManyToManyField', [], {'default': '[]', 'to': "orm['main.Policy']", 'symmetrical': 'False'}), + 'schema': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'values': ('picklefield.fields.PickledObjectField', [], {}) + }, + 'main.policyset': { + 'Meta': {'object_name': 'PolicySet'}, + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'policies': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Policy']", 'symmetrical': 'False'}) + }, + 'main.presentation': { + 'Meta': {'object_name': 'Presentation'}, + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}) + }, + 'main.presentationconfiguration': { + 'Meta': {'object_name': 'PresentationConfiguration'}, + 'data': ('picklefield.fields.PickledObjectField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}) + }, + 'main.presentationmedium': { + 'Meta': {'object_name': 'PresentationMedium'}, + 'configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'db_entity_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'medium': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Medium']"}), + 'medium_context': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'presentation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Presentation']"}), + 'rendered_medium': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'visible_attributes': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}) + }, + 'main.primarycomponent': { + 'Meta': {'object_name': 'PrimaryComponent', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.primarycomponentpercent': { + 'Meta': {'object_name': 'PrimaryComponentPercent'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'placetype_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponent']"}), + 'primary_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PrimaryComponent']"}) + }, + 'main.project': { + 'Meta': {'object_name': 'Project', '_ormbases': ['main.ConfigEntity']}, + 'base_year': ('django.db.models.fields.IntegerField', [], {'default': '2005'}), + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.region': { + 'Meta': {'object_name': 'Region', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.report': { + 'Meta': {'object_name': 'Report', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.result': { + 'Meta': {'object_name': 'Result', '_ormbases': ['main.PresentationMedium']}, + 'presentationmedium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.PresentationMedium']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.resultlibrary': { + 'Meta': {'object_name': 'ResultLibrary', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.sacoglanduse': { + 'Meta': {'object_name': 'SacogLandUse'}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'land_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.SacogLandUseDefinition']"}) + }, + 'main.sacoglandusedefinition': { + 'Meta': {'object_name': 'SacogLandUseDefinition'}, + 'attached_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'detached_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'land_use': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_du_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'max_emp_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'min_du_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ind': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_gov': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_med': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_off': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_svc': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_other': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_edu': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_gov': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_med': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_rest': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_ret': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_svc': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'rural_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'main.scaglanduse': { + 'Meta': {'object_name': 'ScagLandUse'}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'land_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ScagLandUseDefinition']"}) + }, + 'main.scaglandusedefinition': { + 'Meta': {'object_name': 'ScagLandUseDefinition'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'land_use': ('django.db.models.fields.IntegerField', [], {}), + 'land_use_description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'land_use_type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}) + }, + 'main.scenario': { + 'Meta': {'object_name': 'Scenario', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}), + 'year': ('django.db.models.fields.IntegerField', [], {}) + }, + 'main.sorttype': { + 'Meta': {'object_name': 'SortType'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'order_by': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True', 'null': 'True'}) + }, + 'main.streetattributeset': { + 'Meta': {'object_name': 'StreetAttributeSet'}, + 'block_size': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lane_width': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'number_of_lanes': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}) + }, + 'main.style': { + 'Meta': {'object_name': 'Style'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'identifier': ('django.db.models.fields.TextField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'style_property': ('django.db.models.fields.TextField', [], {}), + 'target': ('django.db.models.fields.TextField', [], {}) + }, + 'main.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.taz': { + 'Meta': {'object_name': 'Taz'}, + 'geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'source_id': ('django.db.models.fields.IntegerField', [], {'max_length': '200', 'db_index': 'True'}), + 'source_table_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'main.template': { + 'Meta': {'object_name': 'Template', '_ormbases': ['main.Medium']}, + 'medium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Medium']", 'unique': 'True', 'primary_key': 'True'}), + 'template_context': ('picklefield.fields.PickledObjectField', [], {}) + }, + 'main.tilestacheconfig': { + 'Meta': {'object_name': 'TileStacheConfig'}, + 'config': ('picklefield.fields.PickledObjectField', [], {}), + 'enable_caching': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '50'}) + }, + 'main.vmt': { + 'Meta': {'object_name': 'Vmt'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + } + } + + complete_apps = ['main'] \ No newline at end of file diff --git a/footprint/main/migrations/0004_auto__add_field_layer_create_from_selection.py b/footprint/main/migrations/0004_auto__add_field_layer_create_from_selection.py new file mode 100644 index 000000000..69e94a850 --- /dev/null +++ b/footprint/main/migrations/0004_auto__add_field_layer_create_from_selection.py @@ -0,0 +1,623 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Layer.create_from_selection' + db.add_column('main_layer', 'create_from_selection', + self.gf('django.db.models.fields.BooleanField')(default=False), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Layer.create_from_selection' + db.delete_column('main_layer', 'create_from_selection') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.basescenario': { + 'Meta': {'object_name': 'BaseScenario', '_ormbases': ['main.Scenario']}, + 'scenario_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Scenario']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.buildingattributeset': { + 'Meta': {'object_name': 'BuildingAttributeSet'}, + 'building_uses': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuildingUseDefinition']", 'through': "orm['main.BuildingUsePercent']", 'symmetrical': 'False'}), + 'combined_pop_emp_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '4'}), + 'commercial_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'floors': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '7', 'decimal_places': '3'}), + 'gross_net_ratio': ('django.db.models.fields.DecimalField', [], {'default': '1', 'max_digits': '8', 'decimal_places': '7'}), + 'gross_population_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '14', 'decimal_places': '10'}), + 'hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'household_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '14', 'decimal_places': '10'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'impervious_hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'impervious_roof_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'irrigated_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'parking_spaces': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '7', 'decimal_places': '3'}), + 'parking_structure_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pervious_hardscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'residential_average_lot_size': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'residential_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '9', 'decimal_places': '2'}), + 'softscape_and_landscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'total_far': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '10', 'decimal_places': '7'}) + }, + 'main.buildingusedefinition': { + 'Meta': {'object_name': 'BuildingUseDefinition'}, + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingUseDefinition']", 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.buildingusepercent': { + 'Meta': {'object_name': 'BuildingUsePercent'}, + 'building_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingAttributeSet']"}), + 'building_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingUseDefinition']"}), + 'efficiency': ('django.db.models.fields.DecimalField', [], {'default': '0.85', 'max_digits': '6', 'decimal_places': '4'}), + 'floor_area_ratio': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '12', 'decimal_places': '10'}), + 'gross_built_up_area': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '13', 'decimal_places': '3'}), + 'household_size': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '5', 'decimal_places': '3'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'net_built_up_area': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '13', 'decimal_places': '3'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'square_feet_per_unit': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '11', 'decimal_places': '3'}), + 'unit_density': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '16', 'decimal_places': '10'}), + 'vacancy_rate': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '4', 'decimal_places': '3'}) + }, + 'main.builtform': { + 'Meta': {'object_name': 'BuiltForm'}, + 'building_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuildingAttributeSet']", 'null': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'examples': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltFormExample']", 'null': 'True', 'symmetrical': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'media': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'built_form_media'", 'symmetrical': 'False', 'to': "orm['main.Medium']"}), + 'medium': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Medium']", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_built_form': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.BuiltForm']", 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}) + }, + 'main.builtformexample': { + 'Meta': {'object_name': 'BuiltFormExample'}, + 'content': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'url_aerial': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'url_street': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'main.builtformset': { + 'Meta': {'object_name': 'BuiltFormSet'}, + 'built_forms': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltForm']", 'symmetrical': 'False'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.category': { + 'Meta': {'object_name': 'Category'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'value': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.chart': { + 'Meta': {'object_name': 'Chart', '_ormbases': ['main.Result']}, + 'result_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Result']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.configentity': { + 'Meta': {'object_name': 'ConfigEntity'}, + 'bounds': ('django.contrib.gis.db.models.fields.MultiPolygonField', [], {}), + 'built_form_sets': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.BuiltFormSet']", 'symmetrical': 'False'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Category']", 'symmetrical': 'False'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'config_entity_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'db_entities': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.DbEntity']", 'through': "orm['main.DbEntityInterest']", 'symmetrical': 'False'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'media': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Medium']", 'null': 'True', 'symmetrical': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_config_entity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'clone_set'", 'null': 'True', 'to': "orm['main.ConfigEntity']"}), + 'parent_config_entity': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parent_set'", 'null': 'True', 'to': "orm['main.ConfigEntity']"}), + 'policy_sets': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PolicySet']", 'symmetrical': 'False'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'selections': ('footprint.main.models.config.model_pickled_object_field.SelectionModelsPickledObjectField', [], {'default': "{'db_entities': {}, 'sets': {}}"}), + 'updater': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'config_entity_updater'", 'null': 'True', 'to': "orm['auth.User']"}) + }, + 'main.core': { + 'Meta': {'object_name': 'Core'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + }, + 'main.dbentity': { + 'Meta': {'object_name': 'DbEntity'}, + 'class_key': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'db_entity_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'extent_authority': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'feature_class_configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'group_by': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'hosts': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']", 'null': 'True'}), + 'query': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'schema': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'srid': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'table': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'updater': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'db_entity_updater'", 'null': 'True', 'to': "orm['auth.User']"}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '1000', 'null': 'True'}) + }, + 'main.dbentityinterest': { + 'Meta': {'object_name': 'DbEntityInterest'}, + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'db_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']"}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interest': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Interest']"}) + }, + 'main.fiscal': { + 'Meta': {'object_name': 'Fiscal'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + }, + 'main.flatbuiltform': { + 'Meta': {'object_name': 'FlatBuiltForm'}, + 'accommodation_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_agriculture': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_industrial': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_mixed': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_employment_retail': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use_with_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_mixed_use_without_office': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_attached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_multifamily': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_single_family_large_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'acres_parcel_residential_single_family_small_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'agricultural_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'agriculture_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'armed_forces_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'arts_entertainment_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'attached_single_family_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'avg_estimated_building_height_feet': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'block_avg_size_acres': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'building_avg_number_of_floors': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'building_sqft_accommodation': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_arts_entertainment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_attached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_detached_single_family': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_education_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_industrial_non_warehouse': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_medical_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_multifamily_2_to_4': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_multifamily_5_plus': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_office_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_other_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_public_admin': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_restaurant': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_retail_services': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_single_family_large_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_single_family_small_lot': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_total': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_transport_warehouse': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'building_sqft_wholesale': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'built_form_id': ('django.db.models.fields.IntegerField', [], {'primary_key': 'True'}), + 'built_form_type': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'commercial_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'construction_utilities_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'dwelling_unit_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'education_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'employment_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'extraction_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'gross_net_ratio': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '11', 'decimal_places': '10'}), + 'household_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'industrial_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'intersection_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'intersections_sqmi': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'irrigated_percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'null': 'True', 'max_digits': '15', 'decimal_places': '7'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'manufacturing_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'medical_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'multifamily_2_to_4_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'multifamily_5_plus_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'office_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'office_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'other_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'percent_civic': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_employment': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_mixed_use': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_parks': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_residential': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'percent_streets': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '6', 'decimal_places': '5'}), + 'population_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'pt_connectivity': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_density': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_land_use_mix': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'pt_score': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'public_admin_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'residential_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'residential_irrigated_square_feet': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '7'}), + 'restaurant_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'retail_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'retail_services_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'single_family_large_lot_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'single_family_small_lot_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'softscape_and_landscape_percent': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '15', 'decimal_places': '7'}), + 'street_pattern': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'transport_warehouse_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}), + 'wholesale_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '15', 'decimal_places': '10'}) + }, + 'main.futurescenario': { + 'Meta': {'object_name': 'FutureScenario', '_ormbases': ['main.Scenario']}, + 'scenario_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Scenario']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.geolibrary': { + 'Meta': {'object_name': 'GeoLibrary'}, + 'entities': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.DbEntity']", 'through': "orm['main.GeoLibraryCatalog']", 'symmetrical': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'main.geolibrarycatalog': { + 'Meta': {'object_name': 'GeoLibraryCatalog'}, + 'entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.DbEntity']"}), + 'geo_library': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.GeoLibrary']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'position': ('django.db.models.fields.IntegerField', [], {}) + }, + 'main.globalconfig': { + 'Meta': {'object_name': 'GlobalConfig', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.grid': { + 'Meta': {'object_name': 'Grid', '_ormbases': ['main.Result']}, + 'result_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Result']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.gridcell': { + 'Meta': {'object_name': 'GridCell'}, + 'geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'source_id': ('django.db.models.fields.IntegerField', [], {'max_length': '200', 'db_index': 'True'}), + 'source_table_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'main.interest': { + 'Meta': {'object_name': 'Interest'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}) + }, + 'main.job': { + 'Meta': {'ordering': "['-created_on']", 'object_name': 'Job'}, + 'created_on': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'data': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'ended_on': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'hashid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '36'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'status': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'task_id': ('django.db.models.fields.CharField', [], {'max_length': '36'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'to': "orm['auth.User']"}) + }, + 'main.layer': { + 'Meta': {'object_name': 'Layer', '_ormbases': ['main.PresentationMedium']}, + 'create_from_selection': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'origin_instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Layer']", 'null': 'True'}), + 'presentationmedium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.PresentationMedium']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.layerchart': { + 'Meta': {'object_name': 'LayerChart', '_ormbases': ['main.Chart']}, + 'chart_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Chart']", 'unique': 'True', 'primary_key': 'True'}), + 'layer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Layer']"}) + }, + 'main.layerlibrary': { + 'Meta': {'object_name': 'LayerLibrary', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.map': { + 'Meta': {'object_name': 'Map', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.medium': { + 'Meta': {'object_name': 'Medium'}, + 'content': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'content_type': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'main.painting': { + 'Meta': {'object_name': 'Painting', '_ormbases': ['main.Map']}, + 'map_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Map']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.parcel': { + 'Meta': {'object_name': 'Parcel'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'wkb_geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}) + }, + 'main.placetype': { + 'Meta': {'object_name': 'Placetype', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'intersection_density': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'placetype_components': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PlacetypeComponent']", 'through': "orm['main.PlacetypeComponentPercent']", 'symmetrical': 'False'}), + 'street_attributes': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.StreetAttributeSet']", 'null': 'True'}) + }, + 'main.placetypecomponent': { + 'Meta': {'object_name': 'PlacetypeComponent', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'component_category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponentCategory']"}), + 'primary_components': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.PrimaryComponent']", 'through': "orm['main.PrimaryComponentPercent']", 'symmetrical': 'False'}) + }, + 'main.placetypecomponentcategory': { + 'Meta': {'object_name': 'PlacetypeComponentCategory'}, + 'contributes_to_net': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.placetypecomponentpercent': { + 'Meta': {'object_name': 'PlacetypeComponentPercent'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'placetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Placetype']"}), + 'placetype_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponent']", 'null': 'True'}) + }, + 'main.policy': { + 'Meta': {'object_name': 'Policy'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'policies': ('django.db.models.fields.related.ManyToManyField', [], {'default': '[]', 'to': "orm['main.Policy']", 'symmetrical': 'False'}), + 'schema': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'values': ('picklefield.fields.PickledObjectField', [], {}) + }, + 'main.policyset': { + 'Meta': {'object_name': 'PolicySet'}, + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'policies': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Policy']", 'symmetrical': 'False'}) + }, + 'main.presentation': { + 'Meta': {'object_name': 'Presentation'}, + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}) + }, + 'main.presentationconfiguration': { + 'Meta': {'object_name': 'PresentationConfiguration'}, + 'data': ('picklefield.fields.PickledObjectField', [], {}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'scope': ('django.db.models.fields.CharField', [], {'max_length': '120'}) + }, + 'main.presentationmedium': { + 'Meta': {'object_name': 'PresentationMedium'}, + 'configuration': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'db_entity_key': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'medium': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Medium']"}), + 'medium_context': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'presentation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Presentation']"}), + 'rendered_medium': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Tag']", 'symmetrical': 'False'}), + 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'visible_attributes': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}) + }, + 'main.primarycomponent': { + 'Meta': {'object_name': 'PrimaryComponent', '_ormbases': ['main.BuiltForm']}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.primarycomponentpercent': { + 'Meta': {'object_name': 'PrimaryComponentPercent'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percent': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '21', 'decimal_places': '20'}), + 'placetype_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PlacetypeComponent']"}), + 'primary_component': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.PrimaryComponent']"}) + }, + 'main.project': { + 'Meta': {'object_name': 'Project', '_ormbases': ['main.ConfigEntity']}, + 'base_year': ('django.db.models.fields.IntegerField', [], {'default': '2005'}), + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.region': { + 'Meta': {'object_name': 'Region', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.report': { + 'Meta': {'object_name': 'Report', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.result': { + 'Meta': {'object_name': 'Result', '_ormbases': ['main.PresentationMedium']}, + 'presentationmedium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.PresentationMedium']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.resultlibrary': { + 'Meta': {'object_name': 'ResultLibrary', '_ormbases': ['main.Presentation']}, + 'presentation_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Presentation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'main.sacoglanduse': { + 'Meta': {'object_name': 'SacogLandUse'}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'land_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.SacogLandUseDefinition']"}) + }, + 'main.sacoglandusedefinition': { + 'Meta': {'object_name': 'SacogLandUseDefinition'}, + 'attached_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'detached_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'land_use': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'max_du_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'max_emp_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'min_du_ac': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ind': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_gov': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_med': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_off': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_off_svc': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_other': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_edu': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_gov': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_pub_med': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_rest': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_ret': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'pct_ret_svc': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '9', 'decimal_places': '2'}), + 'rural_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'main.scaglanduse': { + 'Meta': {'object_name': 'ScagLandUse'}, + 'builtform_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.BuiltForm']", 'unique': 'True', 'primary_key': 'True'}), + 'land_use_definition': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ScagLandUseDefinition']"}) + }, + 'main.scaglandusedefinition': { + 'Meta': {'object_name': 'ScagLandUseDefinition'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'land_use': ('django.db.models.fields.IntegerField', [], {}), + 'land_use_description': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'land_use_type': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}) + }, + 'main.scenario': { + 'Meta': {'object_name': 'Scenario', '_ormbases': ['main.ConfigEntity']}, + 'configentity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.ConfigEntity']", 'unique': 'True', 'primary_key': 'True'}), + 'year': ('django.db.models.fields.IntegerField', [], {}) + }, + 'main.sorttype': { + 'Meta': {'object_name': 'SortType'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'order_by': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True', 'null': 'True'}) + }, + 'main.streetattributeset': { + 'Meta': {'object_name': 'StreetAttributeSet'}, + 'block_size': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'lane_width': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}), + 'number_of_lanes': ('django.db.models.fields.DecimalField', [], {'default': '0', 'max_digits': '8', 'decimal_places': '4'}) + }, + 'main.style': { + 'Meta': {'object_name': 'Style'}, + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'identifier': ('django.db.models.fields.TextField', [], {}), + 'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '120'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'style_property': ('django.db.models.fields.TextField', [], {}), + 'target': ('django.db.models.fields.TextField', [], {}) + }, + 'main.tag': { + 'Meta': {'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'tag': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'main.taz': { + 'Meta': {'object_name': 'Taz'}, + 'geometry': ('django.contrib.gis.db.models.fields.GeometryField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'source_id': ('django.db.models.fields.IntegerField', [], {'max_length': '200', 'db_index': 'True'}), + 'source_table_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}) + }, + 'main.template': { + 'Meta': {'object_name': 'Template', '_ormbases': ['main.Medium']}, + 'medium_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.Medium']", 'unique': 'True', 'primary_key': 'True'}), + 'template_context': ('picklefield.fields.PickledObjectField', [], {}) + }, + 'main.tilestacheconfig': { + 'Meta': {'object_name': 'TileStacheConfig'}, + 'config': ('picklefield.fields.PickledObjectField', [], {}), + 'enable_caching': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '50'}) + }, + 'main.vmt': { + 'Meta': {'object_name': 'Vmt'}, + 'celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'completed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'config_entity': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.ConfigEntity']"}), + 'failed': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'previous_celery_task': ('picklefield.fields.PickledObjectField', [], {'null': 'True'}), + 'started': ('django.db.models.fields.DateField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + } + } + + complete_apps = ['main'] \ No newline at end of file diff --git a/footprint/main/migrations/__init__.py b/footprint/main/migrations/__init__.py new file mode 100644 index 000000000..18a35f6bd --- /dev/null +++ b/footprint/main/migrations/__init__.py @@ -0,0 +1,3 @@ +import logging +south_logger = logging.getLogger('south') +south_logger.setLevel(logging.INFO) \ No newline at end of file diff --git a/footprint/main/mixins/__init__.py b/footprint/main/mixins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/mixins/building_aggregate.py b/footprint/main/mixins/building_aggregate.py new file mode 100644 index 000000000..e2c009bf1 --- /dev/null +++ b/footprint/main/mixins/building_aggregate.py @@ -0,0 +1,195 @@ +# This Python file uses the following encoding: utf-8 + +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +import itertools + +__author__ = 'calthorpe_associates' + +from django.contrib.gis.db import models +from footprint.main.models.built_form.building_use_percent import BuildingUsePercent +from django.db.models import Sum +from collections import defaultdict + + +class BuildingAttributeAggregate(models.Model): + """ + An abstract class that describes a high-level built form that has a + :model:`built_forms.building_attributes.BuildingAttributeSet`, defines methods to aggregate attributes + the classes :model:`built_forms.buildingtype.BuildingType` and :model:`built_forms.placetype.Placetype` + according to the mix of their components. + """ + class Meta: + abstract = True + app_label = 'main' + + def building_attributes_list(self): + """ + Defines the basic required attributes of + core attributes + +++++++++++++++ + - floors + - residential_average_lot_size + - softscape_and_landscape_percent + - pervious_hardscape_percent + - impervious_hardscape_percent + - total_far + - impervious_roof_percent + - parking_structure_square_feet + - parking_spaces + - irrigated_percent + """ + + return ['floors', + 'residential_average_lot_size', + 'softscape_and_landscape_percent', + 'pervious_hardscape_percent', + 'impervious_hardscape_percent', + 'total_far', + 'impervious_roof_percent', + 'parking_structure_square_feet', + 'parking_spaces', + 'irrigated_percent', + ] + + def complete_aggregate_definition(self): + """ + Checks that all the components have been established, to avoid redundant calculations + :return: True or False + """ + + if self.get_all_components().aggregate(Sum('percent'))['percent__sum'] < .98: + return False + else: + return True + + def get_all_components(self): + """ + Identifies the component class and returns the component_percent objects representing the relationship + between the components and the BuildingAggregate + :return: BuildingAggregate component_percents + """ + component_percent_field = self.get_component_field().through.__name__ + component_percents = getattr(self, "{0}_set".format(component_percent_field.lower())).all() + return component_percents + + def aggregate_built_form_attributes(self): + """ + Grabs the component buildings of the BuildingAggregate and does a weighted average of their core attributes, + before passing the building_attributes to the calculate_derived_fields() method. + """ + if not self.complete_aggregate_definition(): + return + + self.building_attributes.gross_net_ratio = self.calculate_gross_net_ratio() + + for attribute in self.building_attributes_list(): + attribute_value = sum([ + (getattr(component_percent.component().building_attributes, attribute) or 0) * + component_percent.percent for component_percent in self.get_all_components() + ]) + + setattr(self.building_attributes, attribute, attribute_value) + + self.building_attributes.save() + self.aggregate_built_form_uses() + self.building_attributes.calculate_derived_fields() + self.save() + + def aggregate_built_form_uses(self): + """ + Aggregates the attributes of the :model:`main.BuildingUsePercent` objects associated with the components + of the aggregate built form, and creates new :model:`main.BuildingUsePercent` objects associated with the + aggregate object. + + For each type of use (single family large lot, restaurant, etc) that is present in the components of the + aggregate being assembled, a new BuildingUsePercent object is created and connected to the aggregate's + BuildingAttributes. Attributes of the new BuildingUsePercent object are derived from the attributes of + BuildingUsePercent objects in the components. + + Attributes are averaged with the method: + Σ(attribute_value * component_use_percent * component_percent) / aggregate_use_percent + + where + component_percent = the percent of the component within the aggregate + component_use_percent = the percent of the use within the component + aggregate_use_percent = the percent of the use within the aggregate + + for each use matching the current type of use in all of the components of the aggregate + + """ + + def get_component_percent(use_percent, component_percents): + """ + gets the component_percent for the component_use_percent + :param use_percent: + :return: + """ + building_attributes = use_percent.building_attributes + components = [component_percent for component_percent in component_percents + if component_percent.component().building_attributes == building_attributes] + return components[0].percent + + component_percents = self.get_all_components() + + component_use_percents = list(itertools.chain.from_iterable([ + list(component_percent.component().building_attributes.buildingusepercent_set.all()) + for component_percent in component_percents + ])) + + component_use_definitions = set([ + component_use_percent.building_use_definition for component_use_percent in component_use_percents + ]) + + for use_definition in component_use_definitions: + aggregate_use_attributes = defaultdict(lambda: 0.0000000000000000000000000) + + # makes a list of BuildingUsePercent objects of the use currently being aggregated + use_percent_components = [component_use_percent for component_use_percent in component_use_percents + if component_use_percent.building_use_definition.name == use_definition.name] + + aggregate_use_attributes['percent'] = sum([ + component_use_percent.percent * + get_component_percent(component_use_percent, component_percents) + for component_use_percent in use_percent_components + ]) + + attributes = use_definition.get_attributes() + + for attr in attributes: + + if attr in ['household_size', 'vacancy_rate', 'efficiency']: + aggregate_use_attributes[attr] = sum([ + + getattr(component_use_percent, attr) * + component_use_percent.percent * + get_component_percent(component_use_percent, component_percents) + + for component_use_percent in use_percent_components + + ]) / aggregate_use_attributes['percent'] + + else: + aggregate_use_attributes[attr] = sum([ + getattr(component_use_percent, attr) * + get_component_percent(component_use_percent, component_percents) + for component_use_percent in use_percent_components + ]) + + + BuildingUsePercent.objects.update_or_create( + building_attributes=self.building_attributes, + building_use_definition=use_definition, + defaults=aggregate_use_attributes + ) \ No newline at end of file diff --git a/footprint/main/mixins/building_attributes.py b/footprint/main/mixins/building_attributes.py new file mode 100644 index 000000000..faea88cf3 --- /dev/null +++ b/footprint/main/mixins/building_attributes.py @@ -0,0 +1,34 @@ + + +__author__ = 'calthorpe_associates' + +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + + +__author__ = 'calthorpe_associates' + +from django.contrib.gis.db import models +from footprint.main.models.built_form.building_attribute_set import BuildingAttributeSet + +class BuildingAttributes(models.Model): + class Meta: + abstract = True + app_label = 'main' + + building_attributes = models.ForeignKey(BuildingAttributeSet, null=True) + + def attributes(self): + return 'building' + diff --git a/footprint/main/mixins/built_form_sets.py b/footprint/main/mixins/built_form_sets.py new file mode 100644 index 000000000..4daf1db6c --- /dev/null +++ b/footprint/main/mixins/built_form_sets.py @@ -0,0 +1,44 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.built_form.built_form_set import BuiltFormSet +from django.contrib.gis.db import models +__author__ = 'calthorpe_associates' + +class BuiltFormSets(models.Model): + """ + Represents a collection of BuiltFormSets where a default may be specified + """ + built_form_sets = models.ManyToManyField(BuiltFormSet) + + def add_built_form_sets(self, *built_form_sets): + """ + Adds one or more BuiltFormSets to the instance's collection. If the instance has not yet overriden its parents' sets, the parents sets will be adopted and then the given built_form_sets will be added + :return: The computed results after adding the given items + """ + self._add('built_form_sets', *built_form_sets) + + def remove_built_form_sets(self, *built_form_sets): + """ + Removes the given built_form_sets from the instances collection. These may be either items inherited from an ancestor or the instance's own items + :param built_form_sets: + :return: The computed results after removing the given items + """ + self._remove('built_form_sets', *built_form_sets) + + class Meta: + abstract = True diff --git a/footprint/main/mixins/categories.py b/footprint/main/mixins/categories.py new file mode 100644 index 000000000..26d89e710 --- /dev/null +++ b/footprint/main/mixins/categories.py @@ -0,0 +1,41 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.models.category import Category + +__author__ = 'calthorpe_associates' + +class Categories(models.Model): + """ + Mixin applied to classes whose instances are named. Name is a human-friendly name designed for internationalization. + Keys, by contrast, server as ids to resolve database tables. + """ + categories = models.ManyToManyField(Category) + + def add_categories(self, *categories): + """ + Adds the given categories, enforcing unique keys. Incoming category instances replaces those with duplicate keys + :param categories: + :return: + """ + self.categories.remove(*self.categories.filter(key__in=map(lambda category: category.key, categories))) + self.categories.add(*categories) + + class Meta: + abstract = True + diff --git a/footprint/main/mixins/config_entity_related_collection_adoption.py b/footprint/main/mixins/config_entity_related_collection_adoption.py new file mode 100644 index 000000000..2ca99d20e --- /dev/null +++ b/footprint/main/mixins/config_entity_related_collection_adoption.py @@ -0,0 +1,62 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + + +__author__ = 'calthorpe_associates' + +class ConfigEntityRelatedCollectionAdoption(object): + """ + ConfigEntity QuerySet mixin that enables collections of the parent_config_entity to be inherited or merged with the child's own collection + """ + + def computed_policy_sets(self, **query_kwargs): + """ + Return this instance's policy_sets if they are not an empty list, otherwise traverses the parent hierarchy until a non-empty policy_sets list or the GlobalConfig is encountered. The list of that ancestor is returned + :param **query_kwargs - optional filtering to apply to the results + :return: The computed results + """ + return self._computed('policy_sets', **query_kwargs) + + def computed_built_form_sets(self, **query_kwargs): + """ + Return this instance's built_form_sets if they are not an empty list, otherwise traverses the parent hierarchy until a non-empty policy_sets list or the GlobalConfig is encountered. The list of that ancestor is returned + :param **query_kwargs - optional filtering to apply to the results + :return: The computed results + """ + return self._computed('built_form_sets', **query_kwargs) + + def computed_db_entities(self, **query_kwargs): + """ + Return this instance's db_entities_interests if they are not an empty list, otherwise traverses the parent hierarchy until a non-empty db_entities list or the GlobalConfig is encountered. The list of that ancestor is returned + :param **query_kwargs - optional filtering to apply to the results + :return: + """ + db_entities = self._computed_related('db_entities', **query_kwargs) + for db_entity in db_entities: + if db_entity.schema not in self.schema() and db_entity.schema != 'global': + raise Exception("For some reason the DbEntity schema {0} is not part of the ConfigEntity schema hierarchy {1}".format(db_entity.schema, self.schema())) + return db_entities + + def computed_db_entity_interests(self, **query_kwargs): + """ + Like computed_db_entities, but returns the through DbEntityInterest instance instead of the DbEntityInterest + :param query_kwargs: + :return: + """ + return self._computed('db_entities', **query_kwargs) + diff --git a/footprint/main/mixins/config_entity_selection.py b/footprint/main/mixins/config_entity_selection.py new file mode 100644 index 000000000..5bc66f596 --- /dev/null +++ b/footprint/main/mixins/config_entity_selection.py @@ -0,0 +1,237 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.lib.functions import map_to_keyed_collections + +from footprint.main.utils.utils import has_explicit_through_class, foreign_key_field_of_related_class + +__author__ = 'calthorpe_associates' + +# import the logging library +import logging + +# Get an instance of a logger +logger = logging.getLogger(__name__) + +class ConfigEntitySelection(object): + + def select_policy_set(self, policy_set): + """ + Stores a selected policy_set from one of those among computed_policy_sets(). This enables the use of selected_policy_set. Some classes, such as Scenario, must have a selected_policy_sets to carry out certain function. Selecting a policy_set has no bearing on the computed_policy_sets. However, selected_policy_set will default to the only set if only one exists in computed_policy_sets. If multiple exist and none are selected, selected_policy_set will raise an error + :return: + """ + self._select('policy_sets', policy_set) + + def deselect_policy_set(self): + """ + Exactly the opposite of select_policy_set. No set will be selected after this is called, whether or not one was selected, unless there is exactly on set returned by computed_policy_sets() + :return: + """ + self.selections['sets']['policy_sets'] = None + + def selected_policy_set(self): + """ + Returns the policy_set selected by select_policy_set, or the only policy_set if self.computed_policy_sets has exactly one set + :return: the selected policy_set + """ + return self._selected('policy_sets') + + def select_built_form_set(self, built_form_set): + """ + Stores a selected built_form_set from one of those among computed_built_form_sets(). This enables the use of selected_built_form_set. Some classes, such as Scenario, must have a selected_built_form_sets to carry out certain function. Selecting a built_form_set has no bearing on the built_form_sets. However, selected_built_form_set will default to the only set if only one exists in computed_built_form_sets. If multiple exist and none are selected, selected_built_form_set will raise an error + :return: + """ + self._select('built_form_sets', built_form_set) + + def deselect_built_form_set(self): + """ + Exactly the opposite of select_built_form_set. No set will be selected after this is called, whether or not one was selected, unless there is exactly on set returned by computed_built_form_sets() + :return: + """ + self.selections['sets']['built_form_sets'] = None + + def selected_built_form_set(self): + """ + Returns the built_form_set selected by built_form_set, or the only built_form_set if self.computed_built_form_sets has exactly one set + :return: the selected built_form_set + """ + return self._selected('built_form_sets') + + def selected_db_entities(self, **query_kwargs): + """ + Like computed_db_entities, but only returns one DbEntity per unique DbEntity key based on which DbEntity in each shared key set is selected (or the only one for unshared keys) + :param **query_kwargs - optional filtering to apply to the results + :return: + """ + db_entities = self._computed_related('db_entities', **query_kwargs) + # There's probably an easier way to do this, but checking pks is the most obvious + selected_db_entity_pks = map(lambda db_entity: db_entity.pk, + filter(lambda db_entity: self._is_selected_of_shared_key('db_entities', db_entity), + db_entities)) + return db_entities.filter(pk__in=selected_db_entity_pks) + + def selected_db_entity_interests(self, **query_kwargs): + db_entities = self.selected_db_entities(**query_kwargs) + return self.dbentityinterest_set.filter(db_entity__in=db_entities) + + def select_db_entity_of_key(self, db_entity_key, instance): + return self._select_among_shared_key('db_entities', db_entity_key, instance) + + def selected_db_entity(self, db_entity_key): + """ + A ConfigEntity may reference multiple DbEntities with the same key via DbEntityInterest. One of those with the same key may be marked as selected, or if only one exists it is considered selected. This method returns the 'selected' DbEntity of the given db_entity_key. + :param db_entity_key + :return: The selected DbEntity of the given db_entity_key + """ + return self._selected_of_shared_key('db_entities', db_entity_key) + + def selected_db_entity_interest(self, db_entity_key): + """ + A ConfigEntity may reference multiple DbEntities with the same key via DbEntityInterest. One of those with the same key may be marked as selected, or if only one exists it is considered selected. This method returns the 'selected' DbEntity of the given db_entity_key. + :param db_entity_k + :return: The DbEntityInterest of the selected DbEntity + """ + selected_db_entity = self._selected_of_shared_key('db_entities', db_entity_key) + return self.dbentityinterest_set.filter(db_entity=selected_db_entity)[0] + + def _select_implicit_defaults(self, attribute): + """ + Inspect all of the items of the given shared-key collection and select those with unique keys to make them the default for that key. This insures that every key has a defaults in the selections[key'] dict + :return: None + """ + items_by_key = map_to_keyed_collections(lambda item: item.key, self._computed_related(attribute)) + for key, items in items_by_key.iteritems(): + if len(items)==1: + # Single items always become the selected item for that key + self._select_among_shared_key(attribute, key, items[0]) + else: + # All keys that are shared should have something selected already + try: + selection = self._selected_of_shared_key(attribute, key) + except: + # This shouldn't happen, but log a warning and set the first item as the selected item + logger.warning("Instance {0} has no {1} with key {2} does not return exactly one set, but returns {3}".format( + self, + attribute, + key, + len(items))) + self._select_among_shared_key(attribute, key, items[0]) + + def _select(self, attribute, value): + """ + Selects the given value from among the collection indicated by attribute. The value is indicated by selected in the self.selections['sets'] dict + :param attribute: The name of a collection of the instance, such as 'built_forms' + :param value: An collection value that must be part of the collection indicated by the attribute + :return: + """ + if not value in self._computed_related(attribute): + raise Exception("For instance {0}, {1} is not among the {2} in {3}".format( + self, + value, + "computed_{0}".format(attribute), + self._computed_related(attribute))) + # Store the id here to prevent the entire object from being pickled + self.selections['sets'][attribute] = value + + def _selected(self, attribute): + """ + Returns the selected value of the collection indicated by attribute. Raises an exception if selected value exists and there is not exactly one value in the collection. + :param attribute: The name of a collection of the instance, such as 'built_forms" + :return: The selected value or the only value of the collection if its a one-item collection + """ + selection = self.selections['sets'].get(attribute, None) + if selection: + return selection + if len(self._computed_related(attribute))==1: + return self._computed_related(attribute)[0] + raise Exception("Instance {0} has no {1} selected does not return exactly one set, but returns {2}".format( + self, + attribute, + len(self._computed_related(attribute)))) + + def _is_selected(self, attribute, value): + """ + Returns true if the value of the collection indicated by the given attribute is the value selected for that collection + :param attribute: + :param value: + :return: + """ + return self._selected(attribute) == value + + def query_prefix(self, attribute): + """ + Get the key query name based on if the many-to-many attribute has an explicit through class or not + :param attribute: + :return: + """ + if has_explicit_through_class(self, attribute): + return "{0}__".format(foreign_key_field_of_related_class( + self.many_field(attribute).through, + self.many_field(attribute).model).name) + else: + return '' + + def _select_among_shared_key(self, attribute, key, value): + """ + For ManyToMany attributes that mixin SharedKey, it's possible to have multiple items with the same key. With this method, one of the items with the same key can be selected, adding it to the self.selected dict. + :param attribute: The attribute name of the ManyToMany field + :param key: The common item key + :param value: The item among the items with the same key to be selected + :return: + """ + prefix = '' #self.query_prefix(attribute) + kwargs = {"{0}{1}".format(prefix, 'key'):key, "{0}{1}".format(prefix, 'pk'):value.pk} + if not len(self._computed_related(attribute, **kwargs))==1: + raise Exception("For instance {0}, many-to-many item {1} with key {2} and pk {3} is not among the {4} in {5}".format( + self, + value, + key, + value.pk, + "computed_{0}".format(attribute), + self._computed_related(attribute))) + self.selections[attribute][key] = value + + def _selected_of_shared_key(self, attribute, key): + """ + Selects the ManyToMany item of the given attribute name that is selected for the given key. An exception is through if multiple items with the given key exist but non have been selected using _select_among_shared_key + :param attribute: The name of a ManyToMany attribute of the instance + :param key: A shared key string of the SharedKey mixin field + :return: The item previously selected by _select_among_shared_key, or the only item with the given key if only one item exists. + """ + selection = self.selections[attribute].get(key, None) + if selection: + return selection + prefix = '' #self.query_prefix(attribute) + kwargs = {"{0}{1}".format(prefix, 'key'):key} + instances = self._computed_related(attribute, **kwargs) + if len(instances)==1: + return instances[0] + raise Exception("Instance {0} has a selected item problem for attribute {1} with key {2}. It does not return exactly one item, but returns {3}".format( + self, + attribute, + key, + len(self._computed_related(attribute, **kwargs)))) + + def _is_selected_of_shared_key(self, attribute, keyed_value): + """ + Returns true if the given value of the given collection attribute is the selected instance for the values's key, as determined by _select_among_shared_key() or if it's the only instance of that key + :param attribute: The attribute of a collection with shared keys, such as 'db_entities' + :param keyed_value: A value of the collection indicated by attribute, having a key property + :return: + """ + return self._selected_of_shared_key(attribute, keyed_value.key)==keyed_value + diff --git a/footprint/main/mixins/db_entities.py b/footprint/main/mixins/db_entities.py new file mode 100644 index 000000000..0e9f85d8b --- /dev/null +++ b/footprint/main/mixins/db_entities.py @@ -0,0 +1,44 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models + +__author__ = 'calthorpe_associates' + +class DbEntities(models.Model): + + db_entities = models.ManyToManyField('DbEntity', through='DbEntityInterest') + + def add_db_entity_interests(self, *db_entity_interests): + """ + Adds one or more unsaved DbEntityInterests to the instance's collection. + If the instance has not yet overridden its parents' db_entities by adding at least one DbEntityInterest, + the parents DbEntityInterests will be adopted and then the db_entity_interests give here will be added + :return: + """ + # This check exists to avoid infinite recursion, since db_entity_interests are sometimes added post_config_entity_save handler + if len(db_entity_interests) > 0: + # Even though the field name is db_entities, we need to pass the DbEntityInterests + self._add('db_entities', *db_entity_interests) + # Update the selections property to mark db_entities as selected that have a unique key. + self._select_implicit_defaults('db_entities') + + def remove_db_entity_interests(self, *db_entity_interests): + self._remove('db_entities', *db_entity_interests) + + class Meta: + abstract = True diff --git a/footprint/main/mixins/deletable.py b/footprint/main/mixins/deletable.py new file mode 100644 index 000000000..558125d5c --- /dev/null +++ b/footprint/main/mixins/deletable.py @@ -0,0 +1,17 @@ +from django.db import models + +__author__ = 'calthorpe' + +class Deletable(models.Model): + deleted = models.BooleanField(default=False) + + def handle_post_save_creation_error(self): + """ + All deletable instances handle post_save creation process errors by marking themselves as deleted. + """ + + self.deleted = True + self.save() + + class Meta(object): + abstract = True diff --git a/footprint/main/mixins/geographic.py b/footprint/main/mixins/geographic.py new file mode 100644 index 000000000..3ac58451a --- /dev/null +++ b/footprint/main/mixins/geographic.py @@ -0,0 +1,24 @@ +from footprint.main.models.geographies.geography import Geography + +__author__ = 'calthorpe_associates' + +from django.contrib.gis.db import models + + +class Geographic(models.Model): + """ + a mixin to add a reference to the Geography class + """ + geography = models.ForeignKey(Geography, null=True) + + @classmethod + def geography_type(cls): + return None + + # todo: we don't need to use the layermapping importer for peer tables, so let's only put this on the base tables + # Because of the layer importer we need this even though the geometry is in the Geography instance + #wkb_geometry = models.GeometryField() + + class Meta(object): + abstract = True + diff --git a/footprint/main/mixins/geographic_bounds.py b/footprint/main/mixins/geographic_bounds.py new file mode 100644 index 000000000..0dda4cde0 --- /dev/null +++ b/footprint/main/mixins/geographic_bounds.py @@ -0,0 +1,24 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + +__author__ = 'calthorpe_associates' + +from django.contrib.gis.db import models + +class GeographicBounds(models.Model): + bounds = models.MultiPolygonField() + + class Meta: + abstract = True + diff --git a/footprint/main/mixins/key.py b/footprint/main/mixins/key.py new file mode 100644 index 000000000..16f5028b0 --- /dev/null +++ b/footprint/main/mixins/key.py @@ -0,0 +1,43 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models + +__author__ = 'calthorpe_associates' + +class Key(models.Model): + """ + Mixin applied to classes that use a alpha-numeric key for naming database tables. Keys must not contain spaces. + They represent a human-readable identifier used to test whether instances should be gotten or created. + There is a unique constraint on the key. Use SharedKey for model classes that need multiple instances to share + a key. + """ + key = models.CharField(max_length=120, null=False, blank=False, unique=True) + + def __unicode__(self): + return "key:{0}".format(self.key) + + @classmethod + def unique_key(cls): + """ + Indicates to mixers that the key must be unique. + :return: + """ + return True + + class Meta: + abstract = True \ No newline at end of file diff --git a/footprint/main/mixins/name.py b/footprint/main/mixins/name.py new file mode 100644 index 000000000..b1a2ccf1c --- /dev/null +++ b/footprint/main/mixins/name.py @@ -0,0 +1,35 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +__author__ = 'calthorpe_associates' + +from django.db import models + +class Name(models.Model): + """ + Mixin applied to classes whose instances are named. Name is a human-friendly name designed for internationalization. + Keys, by contrast, serve as ids to resolve database tables. + """ + name = models.CharField(max_length=100, null=False, blank=False) + description = models.TextField(null=True, blank=True) + + def __unicode__(self): + return "name:{0}, description:{1}".format(self.name, self.description) + + class Meta: + abstract = True + diff --git a/footprint/main/mixins/percent.py b/footprint/main/mixins/percent.py new file mode 100644 index 000000000..82bb63b7e --- /dev/null +++ b/footprint/main/mixins/percent.py @@ -0,0 +1,33 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from django.db.models import IntegerField + +__author__ = 'calthorpe_associates' +from django.db import models + + +class Percent(models.Model): + """ + Mixes in a percent field to many-to-many through classes that need to give each member of a set a percent of 100 + """ + percent = models.DecimalField(max_digits=21, decimal_places=20, default=0, null=False) + # percent = models.FloatField(default=0, null=False) + class Meta: + abstract = True + diff --git a/footprint/main/mixins/policy_sets.py b/footprint/main/mixins/policy_sets.py new file mode 100644 index 000000000..fbe1bafa0 --- /dev/null +++ b/footprint/main/mixins/policy_sets.py @@ -0,0 +1,48 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.main.models.config.policy_set import PolicySet + +__author__ = 'calthorpe_associates' + +from django.contrib.gis.db import models + +class PolicySets(models.Model): + """ + Represents a collection of PolicySets where a default may be specified + """ + policy_sets = models.ManyToManyField(PolicySet) + + def add_policy_sets(self, *policy_sets): + """ + Adds one or more PolicySets to the instance's collection. If the instance has not yet overriden its parents' sets, the parents sets will be adopted and then the given built_form_sets will be added. PolicySets matching that of the parent (if the parent's are adopted) will be ignored + :return: The computed results after adding the given items + """ + self._add('policy_sets', *policy_sets) + + def remove_policy_sets(self, *policy_sets): + """ + Removes the given policy_sets from the instances collection. These may be either items inherited from an ancestor or the instance's own items + :param policy_sets: + :return: The computed results after removing the given items + """ + self._remove('policy_sets', *policy_sets) + + class Meta: + abstract = True + diff --git a/footprint/main/mixins/post_save_status.py b/footprint/main/mixins/post_save_status.py new file mode 100644 index 000000000..8a55d5b2c --- /dev/null +++ b/footprint/main/mixins/post_save_status.py @@ -0,0 +1,17 @@ +from django.db import models + +__author__ = 'calthorpe' + +class PostSaveStatus(models.Model): + """ + A status to help track asynchronous post-save publishers + """ + post_save_status = models.IntegerField(default=False) + + class Meta(object): + abstract = True + +PostSaveStatus.STATUS_READY_NEW = 2 +PostSaveStatus.STATUS_READY_COMPLETE = 3 +PostSaveStatus.STATUS_BUSY = 4 +PostSaveStatus.STATUS_ERROR = 8 diff --git a/footprint/main/mixins/related_collection_adoption.py b/footprint/main/mixins/related_collection_adoption.py new file mode 100644 index 000000000..456800a75 --- /dev/null +++ b/footprint/main/mixins/related_collection_adoption.py @@ -0,0 +1,307 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.lib.functions import get_list_or_if_empty + +from footprint.main.utils.utils import has_explicit_through_class, foreign_key_field_of_related_class + +__author__ = 'calthorpe_associates' + +class RelatedCollectionAdoption(object): + + def donor(self): + """ + Mixers must override this method to return the donor instance + :return: An instance containing the same ManyToMany fields as self to enable adoption of ManyToMany collection items + """ + raise Exception("No donor defined. Override donor() in the mixer") + + def _add(self, attribute, *values): + """ + Adds a many-to-many instance to this many field. For attributes with explicit through classes, the values must be the through instances. For non-explicit through classes the values must be the normal related instances + :param attribute: the attribute of the many-to-many property + :param values: the values to add. These must be through instances for explicit through classes or else related instances + """ + # Prepare for adding new items by first adopt those of the donor, if needed self._adopt_from_donor(attribute) + self._adopt_from_donor(attribute) + # Add only new items, or replace items with the same key (for non-SharedKey) items + self._add_difference(attribute, *values) + + def _set(self, attribute, *values): + """ + Sets the attribute collection to the given values. Existing values of matching pk (or matching related pk for through instances) are left alone rather than removing them and re-adding. This minimizes the number of changes for observers to mimic, and minimizes database writes + :param attribute: the attribute of the many-to-many property + :param values: the values to set. These must be through instances for explicit through classes or else related instances + """ + # Remove existing values that are not in the incoming values + self._subtract_difference(attribute, *values) + # Prepare for adding new items by first adopt those of the donor, if needed + self._adopt_from_donor(attribute) + # Add only new items, or replace items with the same key (for non-SharedKey) items + self._add_difference(attribute, *values) + + def _remove(self, attribute, *values, **kwargs): + """ + Remove the given values from the collection of attribute, first adopting values from the donor in case the caller wishes to remove adopted values + :param attribute: + :param values: + :param kwargs: The only options is 'skip_adopt', to prevent infinite recursion in internal calls + :return: + """ + + + # Adopt the donor values in case the caller wishes to remove some of those from self + if not kwargs.get('skip_adopt', False): + self._adopt_from_donor(attribute) + if has_explicit_through_class(self, attribute): + # If the Many has an explict through class, we must instead delete the through instances that reference the given values + foreign_key_attribute = foreign_key_field_of_related_class(self.many_field(attribute).through, self.many_field(attribute).model).name + filter = {"{0}__id__in".format(foreign_key_attribute):map(lambda v: v.id, values)} + throughs = self.through_set(attribute).filter(**filter) + self._remove_throughs(attribute, *throughs, **kwargs) + else: + # No explicit through, simply remove the values + getattr(self, attribute).remove(*values) + + def _remove_throughs(self, attribute, *throughs, **kwargs): + """ + Removes the specified through instances + :param attribute: + :param throughs: + :param kwargs: The only options is 'skip_adopt', to prevent infinite recursion in internal calls + :return: + """ + + # Adopt the donor instances in case the user wants to remove some of the donor instances + if not kwargs.get('skip_adopt', False): + self._adopt_from_donor(attribute) + for through in throughs: + through.delete() + + def _clear(self, attribute): + """ + Deletes all instances of the given attribute belonging the self, resulting in it delegating calls to _computed to its donor + :param attribute: + :return: + """ + if has_explicit_through_class(self, attribute): + self._clear_throughs(attribute) + else: + getattr(self, attribute).clear() + + + def _clear_throughs(self, attribute): + """ + Deletes all through instances of the given attribute associated with self + :param attribute: + :return: + """ + throughs = self.through_set(attribute).all() + for through in throughs: + through.delete() + + def _adopt_from_donor(self, attribute, force=False): + """ + Adoption of a parent values takes place only when the child values are empty and about to add or remove a set. Adoption is needed because a child can simply inherit to parent values while it has no overrides, but once it has overrides it must manage the parent values more explicitly. If the parent changes its values after adoption, the child will receive signals from the parent (see self.parent_listeners()) + :param attribute: 'policy_sets', 'built_form_sets', etc + :param force: optionally set to True to force the items of the donor to be added that aren't already adopted. This is used for through-class attributes because saving the initial through class instances makes it seems like self already has its own item attributes, even though the donor attributes haven't been adopted yet So force is used after the initial through class instances are saved + :return: + """ + if self.donor() and (force or len(getattr(self, attribute).all()) == 0): + self._add_difference(attribute, *self.donor()._computed(attribute), from_donor=True) + + def _add_difference(self, attribute, *values, **flags): + """ + Adds only those items not already in the list, according to the pk. If the instance class defines has a Key mixin, then the key attribute will be used to let an incoming instance replace an instance with the same key. For attributes with an explicit through class, values must be the through instances, and their equality to the current items will be tested by the foreign key pk, not the value's pk. + :param attribute: 'policy_sets', etc. + :param values: the related or through instances to add which might match instances already adopted from the donor + :param flags: 'from_donor':True indicates that these values are coming from the 'donor', so shouldn't override Key instances with a matching key + :return: + """ + + # Create functions for the attribute based on whether or not it has an explicit through class + tool = AdoptionTool(self, attribute, **flags) + # Get all values of the many attribute or the through attribute + existing_values = getattr(self, tool.resolved_attribute).all() + + # Handle duplicate keys by removing parent instances with keys that match the child + # This only applies to unique_key implementors + # Remove the items with duplicate keys without adopting first (to prevent infinite recursion) + self._remove(attribute, *tool.matching_existing_values_to_remove(values, existing_values), skip_adopt=True) + + # Get values that don't yet exist according to the pks (and key for unique_key implementors) + new_values = tool.filter_out_duplicates( + values, + getattr(self, tool.resolved_attribute).all()) # call this again in case _remove eliminated duplicates + + if tool.is_through: + for non_existing_value in new_values: + # Set the foreign key relationship to self. This might already be done + setattr(non_existing_value, tool.self_foreign_key_attribute, self) + # Clear the pk in case the value is coming from the donor (via self._adopt_from_donor) + setattr(non_existing_value, 'pk', None) + non_existing_value.save() + else: + getattr(self, attribute).add(*new_values) + + def _subtract_difference(self, attribute, *values): + """ + Removes existing values that are not specified in *values. This is used by _set() + :param attribute: + :param values: + :return: + """ + + # Create functions for the attribute based on whether or not it has an explicit through class + tool = AdoptionTool(self, attribute) + # Get all values of the many attribute or the through attribute + existing_values = getattr(self, tool.resolved_attribute).all() + self._remove(attribute, *tool.unmatched_existing_values(values, existing_values), skip_adopt=True) + + def _get(self, attribute, **kwargs): + """ + Gets an expected instance from the collection named by attr by calling get on the results of _computed(). Thus the instance will be queried for in combination of ancestrial and personal collection items. + TODO what is this supposed to do, throw on no match? + :param attr: + :param kwargs: + :return: + """ + return self._computed(attribute, **kwargs) + + def _computed(self, attribute, **query_kwargs): + """ + Returns this instance's attribute's related values or through values (for attributes with an explicit through class) or the donor's if this instance hasn't overridden its values + :param attribute: 'db_entities', etc. Not the through attribute name (e.g. dbentities_set) + :param **query_kwargs: optionally specify query arguments to use with filter() on the results + :return: this attribute's collection or its parents, with optional filtering applied after + """ + resolved_attribute = self.through_attribute(self.many_field(attribute)) if has_explicit_through_class(self, attribute) else attribute + if self.donor(): + return get_list_or_if_empty( + # Get instance's own Many items + self._filter_computed( + getattr(self, resolved_attribute).filter(deleted=False), + **query_kwargs), + # If none exist get donor's + lambda: self.donor()._computed(attribute, **query_kwargs)) + else: + # No donor is defined so just consider this instance's items + return self._filter_computed(getattr(self, resolved_attribute).filter(deleted=False), **query_kwargs) + + def _filter_computed(self, all, **query_kwargs): + return all.filter(**query_kwargs) if len(query_kwargs) > 0 else all + + def _computed_related(self, attribute, **query_kwargs): + """ + Like _computed, but returns the related item of the through class instances for attributes having a through class. Attributes without an explict through class behave just like _computed() + :param attribute: 'db_entities', etc. Not the through attribute name (e.g. dbentityinterest_set) + :param query_kwargs: optional args to filter the results + :return: The related class instances + """ + if self.donor(): + return get_list_or_if_empty( + # Get instance's own Many items + self._filter_computed( + getattr(self, attribute).filter(deleted=False), + **query_kwargs), + # If none exist get donor's + lambda: self.donor()._computed_related(attribute, **query_kwargs)) + else: + # No donor is defined so just consider this instance's items + return self._filter_computed(getattr(self, attribute).filter(deleted=False), **query_kwargs) + + def through_class(self, attribute): + return self.many_field(attribute).through + + def through_attribute(self, many_field): + """ + Inspects the model field for an ManyToMany attribute with a through class to get the reference to the through instances. TODO this assumes the _set suffix but I know there's a way to override. I'm not sure how to inspect the through class to find the way that it should be referred to that overrides the lowercase_set attribute. It should used self.related_field to figure out the name + :param through_class: The through class, such as DbEntityInterest + :return: the set attr for self, such as 'dbentityinterest_set' + """ + through_class = many_field.through + return "{0}_set".format(through_class._meta.module_name) + + def through_set(self, attribute): + return getattr(self, self.through_attribute(self.many_field(attribute))) + + def many_field(self, attribute): + return getattr(self, attribute) + +class AdoptionTool(object): + + def __init__(self, obj, attribute, **kwargs): + self.obj = obj + self.attribute = attribute + self.from_donor = kwargs.get('from_donor', False) + self.is_through = has_explicit_through_class(self.obj, attribute) + if self.is_through: + self.resolved_attribute = obj.through_attribute(obj.many_field(attribute)) + through_class = obj.through_class(attribute) + self.self_foreign_key_attribute = foreign_key_field_of_related_class(through_class, obj.__class__).name + self.foreign_key_attribute = foreign_key_field_of_related_class(through_class, obj.many_field(attribute).model).name + else: + self.resolved_attribute = attribute + self.self_foreign_key_attribute = self.foreign_key_attribute = None + + self.related = getattr(obj,attribute).model + + def resolve_pk(self, value): + return getattr(value, self.foreign_key_attribute).pk if self.is_through else value.pk + + def resolve_key(self, value): + return getattr(value, self.foreign_key_attribute).key if self.is_through else value.key + + def filter_out_duplicates(self, added_values, existing_values): + """ + If the item class implements Key, filters out matching items matching existing items if flags['from_donor'] is True. We don't want the donor to replace a value having a matching key, because the donees choice for that Key should take precedence. If the value is not from the donor, then it's fine to replace a value having a matching key + :param added_values: + :return: + """ + # Get the existing pks of the many or through items + existing_value_pks = map(lambda value: self.resolve_pk(value), existing_values) + new_values = filter(lambda value: not self.resolve_pk(value) in existing_value_pks, added_values) + if hasattr(self.related, 'unique_key') and self.related.unique_key(): + existing_keys = map(lambda value: self.resolve_key(value), existing_values) + return filter(lambda value: not self.from_donor or self.resolve_key(value) not in existing_keys, new_values) + else: + return new_values + + def matching_existing_values_to_remove(self, new_values, existing_values): + """ + Get rid existing items matching incoming items by key if keys must be unique. Only allow this if the incoming items are not being adopted from the donor, since we never want the donor items to override this instance's. This applies to the corner case where the donor adds or changes its item of a certain key and the adoptor already has an item of that key. Ideally the adoptor would take the change if and only if their item matched the item of the donor being replaced, meaning the adoptor was really just mirroring the donor's item. + :param new_values: incoming values via obj._set or obj._add + :param existing_values: values of the attribute that already exist + :return: the existing_values to be removed because new_values have conflicting keys + """ + + if hasattr(self.related, 'unique_key') and self.related.unique_key(): + incoming_keys = map(lambda value: self.resolve_key(value), new_values) + return filter(lambda value: self.resolve_key(value) in incoming_keys and not self.from_donor, existing_values) + else: + return [] + + def unmatched_existing_values(self, new_values, existing_values): + """ + Find existing values that do not match the new_values according to the result of resolve_pk. For obj._set operations, these unmatched values will need to be removed + :param new_values: values meant to replace the existing_values. + :param existing_values: The existing values of the attribute collections + :return: The unmatched existing values that will need to be removed + """ + new_value_pks = map(lambda value: self.resolve_pk(value), new_values) + return filter(lambda value: self.resolve_key(value.pk) not in new_value_pks, existing_values) + diff --git a/footprint/main/mixins/scoped_key.py b/footprint/main/mixins/scoped_key.py new file mode 100644 index 000000000..2b0aa0736 --- /dev/null +++ b/footprint/main/mixins/scoped_key.py @@ -0,0 +1,56 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.utils.utils import resolve_model + +__author__ = 'calthorpe_associates' + +class ScopedKey(models.Model): + """ + This is just like the Key mixin but it doesn't enforce a unique constraint on the key in the database. The key/scope should be unique per config_entity scope, however, and enforced in code. + """ + key = models.CharField(max_length=120, null=False, blank=False, unique=False) + + # Represents the scope of the key. It should be a ConfigEntity key or something similar + scope = models.CharField(max_length=120, null=False, blank=False, unique=False) + + def __unicode__(self): + return "key:{0}, scope:{1}".format(self.key, self.scope) + + @property + def class_scope(self): + """ + Resolve the actual model class, since it's non-trivial to store in the database + :return: + """ + return resolve_model('footprint.main.{0}'.format(self.scope)) + + def __unicode__(self): + return "key:{0}".format(self.key) + + @classmethod + def unique_key(cls): + """ + Indicates to mixers that the key must be unique in the scope it pertains to. + :return: + """ + return True + + class Meta: + abstract = True + diff --git a/footprint/main/mixins/shared_key.py b/footprint/main/mixins/shared_key.py new file mode 100644 index 000000000..785499447 --- /dev/null +++ b/footprint/main/mixins/shared_key.py @@ -0,0 +1,42 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models + +__author__ = 'calthorpe_associates' + +class SharedKey(models.Model): + """ + Mixin applied to classes that use a alpha-numeric key for naming database tables. Keys must not contain spaces. They represent a human-readable identifier used to lookup by predefined constants. Unlike the Key mixin, this key field has no unique constraint. It is useful when multiple versions of something are available, such as multiple DbEntities, and they should be selected between. + """ + key = models.CharField(max_length=50, null=False, blank=False) + + @classmethod + def unique_key(cls): + """ + Indicates to mixers that the key need not be unique + :return: + """ + return False + + def __unicode__(self): + return "key:{0}".format(self.key) + + class Meta: + abstract = True + diff --git a/footprint/main/mixins/street_attributes.py b/footprint/main/mixins/street_attributes.py new file mode 100644 index 000000000..7bc092578 --- /dev/null +++ b/footprint/main/mixins/street_attributes.py @@ -0,0 +1,33 @@ + +__author__ = 'calthorpe_associates' + +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + + +__author__ = 'calthorpe_associates' + +from django.contrib.gis.db import models +from footprint.main.models.built_form.infrastructure_attribute_set import StreetAttributeSet + +class StreetAttributes(models.Model): + class Meta: + abstract = True + app_label = 'main' + + street_attributes = models.ForeignKey(StreetAttributeSet, null=True) + + def attributes(self): + return 'building' + diff --git a/footprint/main/mixins/tags.py b/footprint/main/mixins/tags.py new file mode 100644 index 000000000..a10ed2ab0 --- /dev/null +++ b/footprint/main/mixins/tags.py @@ -0,0 +1,32 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.models.tag import Tag + +__author__ = 'calthorpe_associates' + +class Tags(models.Model): + """ + Mixin applied to classes whose instances are named. Name is a human-friendly name designed for internationalization. + Keys, by contrast, server as ids to resolve database tables. + """ + tags = models.ManyToManyField(Tag) + + class Meta: + abstract = True + diff --git a/footprint/main/mixins/timestamps.py b/footprint/main/mixins/timestamps.py new file mode 100644 index 000000000..915b7d4dd --- /dev/null +++ b/footprint/main/mixins/timestamps.py @@ -0,0 +1,25 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from django.db.models import DateTimeField + +__author__ = 'calthorpe_associates' + + +class Timestamps(models.Model): + + created = DateTimeField(auto_now_add=True) + updated = DateTimeField(auto_now=True) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/__init__.py b/footprint/main/models/__init__.py new file mode 100644 index 000000000..39c8ab813 --- /dev/null +++ b/footprint/main/models/__init__.py @@ -0,0 +1,111 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +# from constants import Constants + +from south.modelsinspector import add_introspection_rules +add_introspection_rules([], [r"^footprint.main.models.config.model_pickled_object_field.ModelPickledObjectField", + r"^footprint.main.models.config.model_pickled_object_field.SelectionModelsPickledObjectField"]) + +from footprint.main.models.config.model_pickled_object_field import ModelPickledObjectField +from footprint.main.models.config.model_pickled_object_field import SelectionModelsPickledObjectField + +import geospatial +from footprint.main.models.future.core_end_state_feature import CoreEndStateFeature +from footprint.main.models.future.core_end_state_demographic_feature import CoreEndStateDemographicFeature +from footprint.main.models.future.core_increment_feature import CoreIncrementFeature +from footprint.main.models.analysis.fiscal_feature import FiscalFeature + +from footprint.main.models.analysis.vmt_features.vmt_feature import VmtFeature +from footprint.main.models.analysis.vmt_features.vmt_quarter_mile_buffer_feature import VmtQuarterMileBufferFeature +from footprint.main.models.analysis.vmt_features.vmt_one_mile_buffer_feature import VmtOneMileBufferFeature +from footprint.main.models.analysis.vmt_features.vmt_variable_buffer_feature import VmtVariableBufferFeature +from footprint.main.models.analysis.vmt_features.vmt_feature import VmtFeature +from footprint.main.models.analysis.vmt_features.vmt_trip_lengths_feature import VmtTripLengthsFeature + +from footprint.main.models.database.information_schema import PGNamespace + +# These import statements are compulsory. Models will not be recognized without them +# There are some tricks published online to import all classes dynamically, but doing so in +# practice has yet been unsuccessful +from footprint.main.models.built_form.built_form import BuiltForm +from footprint.main.models.geospatial.db_entity import DbEntity +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.models.analysis.energy_water_feature import EnergyWaterFeature +from footprint.main.models.tasks.async_job import Job + +from footprint.main.models.base.base_feature import BaseFeature +from footprint.main.models.base.developable_feature import DevelopableFeature +from footprint.main.models.presentation.layer import Layer +from footprint.main.models.future.future_scenario_feature import FutureScenarioFeature +from footprint.main.models.built_form.flat_built_forms import FlatBuiltForm +from footprint.main.models.base.census_blockgroup import CensusBlockgroup +from footprint.main.models.base.census_block import CensusBlock +from footprint.main.models.base.census_tract import CensusTract + +from footprint.main.models.analysis_module.analysis_module import AnalysisModule +from footprint.main.models.analysis_module.celery_task import CeleryTask +from footprint.main.models.analysis_module.core_module.core import Core +from footprint.main.models.analysis_module.fiscal_module.fiscal import Fiscal +from footprint.main.models.analysis_module.vmt_module.vmt import Vmt + +from footprint.main.models.built_form.building_attribute_set import BuildingAttributeSet +from footprint.main.models.built_form.building_use_definition import BuildingUseDefinition +from footprint.main.models.built_form.building_use_percent import BuildingUsePercent +from footprint.main.models.built_form.primary_component import PrimaryComponent +from footprint.main.models.built_form.primary_component_percent import PrimaryComponentPercent +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from footprint.main.models.built_form.placetype_component_percent import PlacetypeComponentPercent +from footprint.main.models.built_form.placetype import Placetype + +from footprint.main.models.config.db_entity_interest import DbEntityInterest + +from footprint.main.models.config.global_config import GlobalConfig +from footprint.main.models.config.interest import Interest +from footprint.main.models.config.policy_set import PolicySet +from footprint.main.models.config.region import Region +from footprint.main.models.config.project import Project +from footprint.main.models.config.scenario import Scenario +from footprint.main.models.base.base_feature import BaseFeature +from footprint.main.models.base.base_demographic_feature import BaseDemographicFeature +from footprint.main.models.base.cpad_holdings_feature import CpadHoldingsFeature +from footprint.main.models.geographies.geography import Geography +from footprint.main.models.geographies.parcel import Parcel +from footprint.main.models.geographies.grid_cell import GridCell +from footprint.main.models.geographies.taz import Taz +from footprint.main.models.presentation.chart import Chart +from footprint.main.models.presentation.geo_library import GeoLibrary +from footprint.main.models.presentation.geo_library_catalog import GeoLibraryCatalog +from footprint.main.models.presentation.grid import Grid +from footprint.main.models.presentation.layer_chart import LayerChart +from footprint.main.models.presentation.layer_library import LayerLibrary +from footprint.main.models.presentation.map import Map +from footprint.main.models.presentation.medium import Medium +from footprint.main.models.presentation.painting import Painting +from footprint.main.models.presentation.presentation import Presentation +from footprint.main.models.presentation.presentation_medium import PresentationMedium +from footprint.main.models.presentation.report import Report +from footprint.main.models.presentation.result import Result +from footprint.main.models.presentation.result_library import ResultLibrary +from footprint.main.models.presentation.style import Style +from footprint.main.models.presentation.template import Template +from footprint.main.models.presentation.presentation_configuration import PresentationConfiguration +from footprint.main.models.sort_type import SortType +from footprint.main.models.presentation.layer_selection import LayerSelection +from footprint.main.models.presentation.tilestache_config import TileStacheConfig + diff --git a/footprint/main/models/analysis/__init__.py b/footprint/main/models/analysis/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/analysis/energy_water_feature.py b/footprint/main/models/analysis/energy_water_feature.py new file mode 100644 index 000000000..9761d7a0b --- /dev/null +++ b/footprint/main/models/analysis/energy_water_feature.py @@ -0,0 +1,63 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' +from django.db import models + +class EnergyWaterFeature(Feature): + + # # These values are interpreted as percents: 30 => 30% + ResEnrgyNewConst = models.DecimalField("New construction - Percentage reduction from baseline rates by a horizon year", max_digits=5, decimal_places=2, default=30) + ResEnrgyRetro = models.DecimalField("Retrofits - Year-upon-year percentage reduction.", max_digits=3, decimal_places=2, default=0.5) #Retrofits - Year-upon-year percentage reduction. + ResEnrgyReplcmt = models.DecimalField("Replacement - Reset of some percentage of existing units to new standards.", max_digits=3, decimal_places=2, default=0.6) #Replacement - Reset of some percentage of existing units to new standards. + # #Commercial Energy (Electricity & Gas) --------------------------------------------- + ComEnrgyNewConst = models.DecimalField("New construction - Percentage reduction from baseline rates by a horizon year.", max_digits=5, decimal_places=2, default=30) #New construction - Percentage reduction from baseline rates by a horizon year. + ComEnrgyRetro = models.DecimalField("Retrofits - Year-upon-year - percentage reduction.", max_digits=3, decimal_places=2, default=0.8 ) #Retrofits - Year-upon-year - percentage reduction. + ComEnrgyReplcmt = models.DecimalField("Replacement - Reset of some percentage of existing units to new standards.", max_digits=3, decimal_places=2, default=1.0 ) #Replacement - Reset of some percentage of existing units to new standards. + # #Residential Water (Indoor & Outdoor) ---------------------------------------------- + ResWatrNewConst = models.DecimalField("Residential New construction", max_digits=5, decimal_places=2, default=30 ) #New construction + ResWatrRetro = models.DecimalField("Residential Retrofits", max_digits=3, decimal_places=2, default=0.05 ) #Retrofits + ResWatrReplcmt = models.DecimalField("Residential Replacement", max_digits=3, decimal_places=2, default=0.06 ) #Replacement + # #Commercial & Industrial Water (Indoor & Outdoor) ---------------------------------- + ComIndWatrNewConst = models.DecimalField("Commercial New construction", max_digits=5, decimal_places=2, default=30 ) #New construction + ComIndWatrRetro = models.DecimalField("Commercial Retrofits", max_digits=3, decimal_places=2, default=0.08 ) #Retrofits + ComIndWatrReplcmt = models.DecimalField("Commercial Replacement", max_digits=3, decimal_places=2, default=0.2 ) #Replacement + # + # #Additional Parameters ------------------------------------------------------------- + # + ## YearsBasetoHoriz = 45 #Number of years between base and horizon years + # + Water_GPCD_SF = models.IntegerField("Indoor per-capita single family gallons per day", default=80) #Indoor per-capita single family gallons per day + Water_GPCD_MF = models.IntegerField("Indoor per-capita multifamily gallons per day", default=70) #Indoor per-capita multifamily gallons per day: + Water_GPED_Retail = models.IntegerField("Indoor per-employee gallons per day, Retail", default=100) #Indoor per-employee gallons per day, Retail + Water_GPED_Office = models.IntegerField("Indoor per-employee gallons per day, Office", default=50) #Indoor per-employee gallons per day, Office + Water_GPED_Industrial = models.IntegerField("Indoor per-employee gallons per day, Industrial", default=100) #Indoor per-employee gallons per day, Industrial + Water_GPED_School = models.IntegerField("Indoor per-employee gallons per day, School", default=86) #Indoor per-employee gallons per day, School + + # #Industrial energy intensity: Annual Energy use per Employee + ann_ind_elec_peremp = models.FloatField("Annual Industrial Energy Use per Employee: kwh", default=27675.45) #kwh + ann_ind_gas_peremp = models.FloatField("Annual Industrial Energy Use per Employee: thm", default=767.56) #thm + + def __unicode__(self): + return unicode("Energy & Water Policy config for %s" % self.scenario.name) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/analysis/fiscal_feature.py b/footprint/main/models/analysis/fiscal_feature.py new file mode 100644 index 000000000..02f1976f5 --- /dev/null +++ b/footprint/main/models/analysis/fiscal_feature.py @@ -0,0 +1,15 @@ +from django.db import models +from footprint.main.models.geospatial.feature import Feature + + +__author__ = 'calthorpe_associates' + + +class FiscalFeature(Feature): + residential_capital_costs = models.DecimalField(max_digits=14, decimal_places=4, default=0) + residential_operations_maintenance_costs = models.DecimalField(max_digits=14, decimal_places=4, default=0) + residential_revenue = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/analysis/land_type_feature.py b/footprint/main/models/analysis/land_type_feature.py new file mode 100644 index 000000000..05c3590e0 --- /dev/null +++ b/footprint/main/models/analysis/land_type_feature.py @@ -0,0 +1,36 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from django.forms import CharField +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class LandTypeFeature(Feature): + + objects = GeoInheritanceManager() + + # This relationship will be dynamically created for the subclass, so that the through class can be dynamically created + #builtform_percents = models.ManyToManyField(BuiltForm, through='BaseBuiltFormFeaturePercent') + landtype = CharField(null=True) + source_type = CharField(null=True) + source_table = CharField(null=True) + + pass diff --git a/footprint/main/models/analysis/vmt_features/__init__.py b/footprint/main/models/analysis/vmt_features/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/analysis/vmt_features/vmt_feature.py b/footprint/main/models/analysis/vmt_features/vmt_feature.py new file mode 100644 index 000000000..adb7d639a --- /dev/null +++ b/footprint/main/models/analysis/vmt_features/vmt_feature.py @@ -0,0 +1,35 @@ +__author__ = 'calthorpe' + +from django.db import models +from footprint.main.models.geospatial.feature import Feature + + +class VmtFeature(Feature): + + acres_gross = models.DecimalField(max_digits=14, decimal_places=4, default=0) + pop = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + final_prod_hbo = models.DecimalField(max_digits=14, decimal_places=4, default=0) + final_prod_hbw = models.DecimalField(max_digits=14, decimal_places=4, default=0) + final_prod_nhb = models.DecimalField(max_digits=14, decimal_places=4, default=0) + final_attr_hbo = models.DecimalField(max_digits=14, decimal_places=4, default=0) + final_attr_hbw = models.DecimalField(max_digits=14, decimal_places=4, default=0) + final_attr_nhb = models.DecimalField(max_digits=14, decimal_places=4, default=0) + vmt_daily = models.DecimalField(max_digits=14, decimal_places=4, default=0) + vmt_daily_w_trucks = models.DecimalField(max_digits=14, decimal_places=4, default=0) + vmt_daily_per_capita = models.DecimalField(max_digits=14, decimal_places=4, default=0) + vmt_daily_per_hh = models.DecimalField(max_digits=14, decimal_places=4, default=0) + vmt_annual = models.DecimalField(max_digits=14, decimal_places=4, default=0) + vmt_annual_w_trucks = models.DecimalField(max_digits=14, decimal_places=4, default=0) + vmt_annual_per_capita = models.DecimalField(max_digits=14, decimal_places=4, default=0) + vmt_annual_per_hh = models.DecimalField(max_digits=14, decimal_places=4, default=0) + raw_trips_total = models.DecimalField(max_digits=14, decimal_places=4, default=0) + internal_capture_trips_total = models.DecimalField(max_digits=14, decimal_places=4, default=0) + walking_trips_total = models.DecimalField(max_digits=14, decimal_places=4, default=0) + transit_trips_total = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + class Meta(object): + abstract = True + app_label = 'main' \ No newline at end of file diff --git a/footprint/main/models/analysis/vmt_features/vmt_one_mile_buffer_feature.py b/footprint/main/models/analysis/vmt_features/vmt_one_mile_buffer_feature.py new file mode 100644 index 000000000..807a9edef --- /dev/null +++ b/footprint/main/models/analysis/vmt_features/vmt_one_mile_buffer_feature.py @@ -0,0 +1,14 @@ +__author__ = 'calthorpe' + +from django.db import models +from footprint.main.models.geospatial.feature import Feature + + +class VmtOneMileBufferFeature(Feature): + + emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + class Meta(object): + abstract = True + app_label = 'main' +__author__ = 'calthorpe_associates' diff --git a/footprint/main/models/analysis/vmt_features/vmt_quarter_mile_buffer_feature.py b/footprint/main/models/analysis/vmt_features/vmt_quarter_mile_buffer_feature.py new file mode 100644 index 000000000..9a7efa9bb --- /dev/null +++ b/footprint/main/models/analysis/vmt_features/vmt_quarter_mile_buffer_feature.py @@ -0,0 +1,19 @@ +__author__ = 'calthorpe' + +from django.db import models +from footprint.main.models.geospatial.feature import Feature + + +class VmtQuarterMileBufferFeature(Feature): + + acres_parcel_res = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_mixed = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du = models.DecimalField(max_digits=14, decimal_places=4, default=0) + pop = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_ret = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/analysis/vmt_features/vmt_trip_lengths_feature.py b/footprint/main/models/analysis/vmt_features/vmt_trip_lengths_feature.py new file mode 100644 index 000000000..c9b0dbcb2 --- /dev/null +++ b/footprint/main/models/analysis/vmt_features/vmt_trip_lengths_feature.py @@ -0,0 +1,21 @@ +__author__ = 'calthorpe' + +from django.db import models +from footprint.main.models.geospatial.feature import Feature + + +class VmtTripLengthsFeature(Feature): + + productions_hbw = models.DecimalField(max_digits=14, decimal_places=4, default=0) + productions_hbo = models.DecimalField(max_digits=14, decimal_places=4, default=0) + productions_nhb = models.DecimalField(max_digits=14, decimal_places=4, default=0) + attractions_hbw = models.DecimalField(max_digits=14, decimal_places=4, default=0) + attractions_hbo = models.DecimalField(max_digits=14, decimal_places=4, default=0) + attractions_nhb = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_30min_transit = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_45min_transit = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + class Meta(object): + abstract = True + app_label = 'main' + diff --git a/footprint/main/models/analysis/vmt_features/vmt_variable_buffer_feature.py b/footprint/main/models/analysis/vmt_features/vmt_variable_buffer_feature.py new file mode 100644 index 000000000..d776b7012 --- /dev/null +++ b/footprint/main/models/analysis/vmt_features/vmt_variable_buffer_feature.py @@ -0,0 +1,43 @@ +__author__ = 'calthorpe' + +from django.db import models +from footprint.main.models.geospatial.feature import Feature + + +class VmtVariableBufferFeature(Feature): + + distance = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_mixed = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du = models.DecimalField(max_digits=14, decimal_places=4, default=0) + pop = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_ret = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_mf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_00_10 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_10_20 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_20_30 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_30_40 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_40_50 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_50_60 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_60_75 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_75_100 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_75_100 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh_inc_100p = models.DecimalField(max_digits=14, decimal_places=4, default=0) + pop_employed = models.DecimalField(max_digits=14, decimal_places=4, default=0) + pop_age16_up = models.DecimalField(max_digits=14, decimal_places=4, default=0) + pop_age65_up = models.DecimalField(max_digits=14, decimal_places=4, default=0) + productions_hbw = models.DecimalField(max_digits=14, decimal_places=4, default=0) + productions_hbo = models.DecimalField(max_digits=14, decimal_places=4, default=0) + productions_nhb = models.DecimalField(max_digits=14, decimal_places=4, default=0) + attractions_hbw = models.DecimalField(max_digits=14, decimal_places=4, default=0) + attractions_hbo = models.DecimalField(max_digits=14, decimal_places=4, default=0) + attractions_nhb = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_30min_transit = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_45min_transit = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + class Meta(object): + abstract = True + app_label = 'main' \ No newline at end of file diff --git a/footprint/main/models/analysis_module/__init__.py b/footprint/main/models/analysis_module/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/analysis_module/analysis_module.py b/footprint/main/models/analysis_module/analysis_module.py new file mode 100644 index 000000000..68d35ace8 --- /dev/null +++ b/footprint/main/models/analysis_module/analysis_module.py @@ -0,0 +1,56 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from datetime import datetime +from django.contrib.auth.models import User +from django.db import models +from django.db.models import DateField +from picklefield import PickledObjectField +from footprint.main.models.config.config_entity import ConfigEntity + +__author__ = 'calthorpe_associates' + +class AnalysisModule(models.Model): + + config_entity = models.ForeignKey(ConfigEntity, null=False) + # Stores the last celery_task + celery_task = PickledObjectField(null=True) + previous_celery_task = PickledObjectField(null=True) + started = DateField(null=True) + completed = DateField(null=True) + failed = DateField(null=True) + user = models.ForeignKey(User, null=True) + + def start(self): + """ + Runs the module in celery + NOT working yet, use method override + :return: + """ + self.previous_celery_task = self.celery_task + self.celery_task = self.task_class() + # Call run + self.celery_task.apply_async(kwargs=dict( + analysis_module_id=self.id, + analysis_module_class=self.__class__)) + + def cancel_and_restart(self): + pass + + def status(self): + return self.celery_task.status + + def time_since_completion(self): + return datetime.utcnow() - self.completed + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/analysis_module/celery_task.py b/footprint/main/models/analysis_module/celery_task.py new file mode 100644 index 000000000..ec0919c15 --- /dev/null +++ b/footprint/main/models/analysis_module/celery_task.py @@ -0,0 +1,60 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from datetime import datetime +import logging +from celery import Task +from footprint.main.models.config.config_entity import ConfigEntity + +__author__ = 'calthorpe_associates' + +logger = logging.getLogger(__name__) + +# TODO This is intended to provide an object-oriented structure to analytic modules +# It's not in use yet but probably will work +class CeleryTask(Task): + + def resolve_analyis_module(self, **kwargs): + analysis_module_id = kwargs['analysis_module_id'] + analysis_module_class = kwargs['analysis_module_class'] + return analysis_module_class.objects.get(analysis_module_id) + + def on_success(self, retval, task_id, args, kwargs): + analysis_module = self.resolve_analyis_module(**kwargs) + analysis_module.completed = datetime.utcnow() + analysis_module.save() + logger.info("Done executing task %s for ConfigEntity: %s".format(analysis_module, analysis_module.config_entity)) + + def on_failure(self, exc, task_id, args, kwargs, einfo): + analysis_module = self.resolve_analyis_module(**kwargs) + analysis_module.failed = datetime.utcnow() + analysis_module.save() + logger.info("Failed executing task %s for ConfigEntity: %s".format(analysis_module, analysis_module.config_entity)) + + def run(self, *args, **kwargs): + """ + Run by when apply_async is called or a similar starter. Fetches the anlysis_module and config_entity and + calls start with those arguments + :param args: + :param kwargs: + :return: + """ + analysis_module = self.resolve_analyis_module(**kwargs) + config_entity = ConfigEntity.objects.get(analysis_module.config_entity.id).all().select_subclasses() + logger.info("Executing task %s for ConfigEntity: %s".format(analysis_module, analysis_module.config_entity)) + analysis_module.started = datetime.utcnow() + analysis_module.save() + task = self() + analysis_module.celery_task = task + self.start(analysis_module=analysis_module, config_entity=config_entity) + + def start(self, **kwargs): + pass diff --git a/footprint/main/models/analysis_module/core_module/__init__.py b/footprint/main/models/analysis_module/core_module/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/analysis_module/core_module/core.py b/footprint/main/models/analysis_module/core_module/core.py new file mode 100644 index 000000000..f72566f07 --- /dev/null +++ b/footprint/main/models/analysis_module/core_module/core.py @@ -0,0 +1,74 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates # # This program is free software: you can redistribute it and/or modify it under the terms of the # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +import logging +from footprint.celery import app +from footprint.common.utils.websockets import send_message_to_client +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models import Job +from footprint.main.models.analysis_module.core_module.core_update_end_state_demographic_feature import update_end_state_demographic_feature +from footprint.main.models.analysis_module.core_module.core_update_end_state_feature import update_end_state_feature +from footprint.main.models.analysis_module.core_module.core_update_future_scenario_feature import update_future_scenario_feature +from footprint.main.models.analysis_module.core_module.core_update_increment_feature import update_increment_feature +from footprint.main.models.analysis_module.analysis_module import AnalysisModule +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +logger = logging.getLogger(__name__) + +class Core(AnalysisModule): + objects = GeoInheritanceManager() + + def start(self): + job = Job.objects.create( + type="core", + status="New", + user=self.user + ) + job.save() + + task = executeCore.apply_async( + args=[job.hashid, self.user, self.config_entity.subclassed_config_entity], + soft_time_limit=3600, + time_limit=3600, + countdown=1 + ) + + job = Job.objects.get(hashid=job.hashid) + job.task_id = task.id + + class Meta(object): + app_label = 'main' + +# Temporary until we figure out CeleryTask +@app.task +def executeCore(hash_id, user, config_entity): + # Make sure all related models have been created before querying + FeatureClassCreator(config_entity).ensure_dynamic_models() + logger.info("Executing Core using {0}".format(config_entity)) + run_core(config_entity) + logger.info("Done executing Core") + logger.info("Executed Core using {0}".format(config_entity)) + send_message_to_client(user.id, + dict(event='analyticModule{0}Completed'.format(Core.__name__), + config_entity_id=config_entity.id) + ) + + +def run_core(config_entity): + update_future_scenario_feature(config_entity) + update_end_state_feature(config_entity) + update_end_state_demographic_feature(config_entity) + update_increment_feature(config_entity) + from footprint.main.publishing.config_entity_publishing import post_save_config_entity_analytic_run + post_save_config_entity_analytic_run.send(sender=config_entity.__class__, config_entity=config_entity, module='core') diff --git a/footprint/main/models/analysis_module/core_module/core_update_end_state_demographic_feature.py b/footprint/main/models/analysis_module/core_module/core_update_end_state_demographic_feature.py new file mode 100644 index 000000000..cf1569af9 --- /dev/null +++ b/footprint/main/models/analysis_module/core_module/core_update_end_state_demographic_feature.py @@ -0,0 +1,62 @@ +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.query_parsing import annotated_related_feature_class_pk_via_geographies + +__author__ = 'calthorpe' + + +def update_end_state_demographic_feature(config_entity): + end_state_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_FEATURE) + end_state_demographic_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_DEMOGRAPHIC_FEATURE) + census_block_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_CENSUS_BLOCK) + + future_scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE) + + feature = future_scenario_class.objects.filter(dirty_flag=True) + + annotated_features = annotated_related_feature_class_pk_via_geographies(feature, config_entity, [Keys.DB_ABSTRACT_END_STATE_DEMOGRAPHIC_FEATURE, Keys.DB_ABSTRACT_CENSUS_BLOCK, Keys.DB_ABSTRACT_END_STATE_FEATURE]) + + for feature in annotated_features: + + demographic = end_state_demographic_class.objects.get(id=feature.end_state_demographic_feature) + census_block = census_block_class.objects.get(id=feature.census_block) + end_state = end_state_class.objects.get(id=feature.end_state) + + demographic.du_occupancy_rate = end_state.hh / end_state.du if end_state.du > 0 else 0 + demographic.pop_male = end_state.pop * census_block.pop_male_rate + demographic.pop_female = end_state.pop * census_block.pop_female_rate + + demographic.pop_female_age20_64 = end_state.pop * census_block.pop_female_age20_64_rate + demographic.pop_male_age20_64 = end_state.pop * census_block.pop_male_age20_64_rate + demographic.pop_age16_up = end_state.pop * census_block.pop_age16_up_rate + demographic.pop_age25_up = end_state.pop * census_block.pop_age25_up_rate + demographic.pop_age65_up = end_state.pop * census_block.pop_age65_up_rate + + demographic.pop_age20_64 = end_state.pop * (census_block.pop_female_age20_64_rate + + census_block.pop_female_age20_64_rate) + + demographic.pop_hs_not_comp = end_state.pop * census_block.pop_hs_not_comp_rate + demographic.pop_hs_diploma = end_state.pop * census_block.pop_hs_diploma_rate + demographic.pop_some_college = end_state.pop * census_block.pop_assoc_some_coll_rate + demographic.pop_college_degree = end_state.pop * census_block.pop_coll_degree_rate + demographic.pop_graduate_degree = end_state.pop * census_block.pop_grad_degree_rate + demographic.pop_employed = end_state.pop * census_block.pop_employed_rate + demographic.hh_inc_00_10 = end_state.hh * census_block.hh_inc_00_10_rate + demographic.hh_inc_10_20 = end_state.hh * census_block.hh_inc_10_20_rate + demographic.hh_inc_20_30 = end_state.hh * census_block.hh_inc_20_30_rate + demographic.hh_inc_30_40 = end_state.hh * census_block.hh_inc_30_40_rate + demographic.hh_inc_40_50 = end_state.hh * census_block.hh_inc_40_50_rate + demographic.hh_inc_50_60 = end_state.hh * census_block.hh_inc_50_60_rate + demographic.hh_inc_60_75 = end_state.hh * census_block.hh_inc_60_75_rate + demographic.hh_inc_75_100 = end_state.hh * census_block.hh_inc_75_100_rate + demographic.hh_inc_100_125 = end_state.hh * census_block.hh_inc_100_125_rate + demographic.hh_inc_125_150 = end_state.hh * census_block.hh_inc_125_150_rate + demographic.hh_inc_150_200 = end_state.hh * census_block.hh_inc_150_200_rate + demographic.hh_inc_200p = end_state.hh * census_block.hh_inc_200p_rate + demographic.hh_avg_vehicles = census_block.hh_agg_veh_rate + demographic.hh_avg_size = end_state.hh * census_block.hh_inc_200p_rate + demographic.hh_agg_inc = end_state.hh * census_block.hh_agg_inc_rate + demographic.hh_avg_inc = census_block.hh_agg_inc_rate + demographic.hh_owner_occ = end_state.hh * census_block.hh_own_occ_rate + demographic.hh_rental_occ = end_state.hh * census_block.hh_rent_occ_rate + + demographic.save() diff --git a/footprint/main/models/analysis_module/core_module/core_update_end_state_feature.py b/footprint/main/models/analysis_module/core_module/core_update_end_state_feature.py new file mode 100644 index 000000000..3032bf245 --- /dev/null +++ b/footprint/main/models/analysis_module/core_module/core_update_end_state_feature.py @@ -0,0 +1,225 @@ +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.query_parsing import annotated_related_feature_class_pk_via_geographies + +__author__ = 'calthorpe' + + +def update_end_state_feature(config_entity): + future_scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE) + developable_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_FEATURE) + base_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_FEATURE) + end_state_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_FEATURE) + + features = future_scenario_class.objects.filter(dirty_flag=True) + + annotated_features = annotated_related_feature_class_pk_via_geographies(features, config_entity, [Keys.DB_ABSTRACT_DEVELOPABLE, Keys.DB_ABSTRACT_BASE_FEATURE, Keys.DB_ABSTRACT_END_STATE_FEATURE]) + + for feature in annotated_features: + end_state = end_state_class.objects.get(id=feature.end_state) + developable = developable_class.objects.get(id=feature.developable) + base = base_class.objects.get(id=feature.base_feature) + + end_state.built_form_id = feature.built_form_id + + end_state.intersection_density_sqmi = feature.intersection_density_sqmi if feature.total_redev is True else \ + feature.intersection_density_sqmi + feature.dev_pct * base.intersection_density_sqmi + + end_state.acres_parcel = feature.acres_parcel if feature.total_redev is True else \ + feature.acres_parcel - (feature.dev_pct * developable.acres_parcel) + base.acres_parcel + + end_state.acres_parcel_res = feature.acres_parcel_res if feature.total_redev \ + is True else feature.acres_parcel_res - (feature.dev_pct * developable.acres_parcel_res) + \ + base.acres_parcel_res + + end_state.acres_parcel_res_detsf_ll = feature.acres_parcel_res_detsf_ll if \ + feature.total_redev is True else feature.acres_parcel_res_detsf_ll \ + - (feature.dev_pct * developable.acres_parcel_res_detsf_ll) + \ + base.acres_parcel_res_detsf_ll + + end_state.acres_parcel_res_detsf_sl = feature.acres_parcel_res_detsf_sl if \ + feature.total_redev is True else feature.acres_parcel_res_detsf_sl \ + - (feature.dev_pct * developable.acres_parcel_res_detsf_sl) + \ + base.acres_parcel_res_detsf_sl + + end_state.acres_parcel_res_attsf = feature.acres_parcel_res_attsf if \ + feature.total_redev is True else feature.acres_parcel_res_attsf - \ + (feature.dev_pct * developable.acres_parcel_res_attsf) + \ + base.acres_parcel_res_attsf + + end_state.acres_parcel_res_mf = feature.acres_parcel_res_mf if feature.total_redev \ + is True else feature.acres_parcel_res_mf - (feature.dev_pct * developable.acres_parcel_res_mf) + \ + base.acres_parcel_res_mf + + end_state.acres_parcel_emp = feature.acres_parcel_emp if feature.total_redev \ + is True else feature.acres_parcel_emp - (feature.dev_pct * developable.acres_parcel_emp) + \ + base.acres_parcel_emp + + end_state.acres_parcel_emp_ret = feature.acres_parcel_emp_ret if feature.total_redev is True else \ + feature.acres_parcel_emp_ret - (feature.dev_pct * developable.acres_parcel_emp_ret) + base.acres_parcel_emp_ret + + end_state.acres_parcel_emp_off = feature.acres_parcel_emp_off if feature.total_redev is True else \ + feature.acres_parcel_emp_off - (feature.dev_pct * developable.acres_parcel_emp_off) + base.acres_parcel_emp_off + + end_state.acres_parcel_emp_ind = feature.acres_parcel_emp_ind if feature.total_redev is True else \ + feature.acres_parcel_emp_ind - (feature.dev_pct * developable.acres_parcel_emp_ind) + base.acres_parcel_emp_ind + + end_state.acres_parcel_emp_ag = feature.acres_parcel_emp_ag if feature.total_redev is True else \ + feature.acres_parcel_emp_ag - (feature.dev_pct * developable.acres_parcel_emp_ag) + base.acres_parcel_emp_ag + + end_state.acres_parcel_emp_mixed = feature.acres_parcel_emp_mixed if feature.total_redev is True else \ + feature.acres_parcel_emp_mixed - (feature.dev_pct * developable.acres_parcel_emp_mixed) + base.acres_parcel_emp_mixed + + end_state.acres_parcel_mixed = feature.acres_parcel_mixed if feature.total_redev is True else \ + feature.acres_parcel_mixed - (feature.dev_pct * developable.acres_parcel_mixed) + base.acres_parcel_mixed + + end_state.acres_parcel_mixed_w_off = feature.acres_parcel_mixed_w_off if feature.total_redev is True else \ + feature.acres_parcel_mixed_w_off - (feature.dev_pct * developable.acres_parcel_mixed_w_off) + base.acres_parcel_mixed_w_off + + end_state.acres_parcel_mixed_no_off = feature.acres_parcel_mixed_no_off if feature.total_redev is True else \ + feature.acres_parcel_mixed_no_off - (feature.dev_pct * developable.acres_parcel_mixed_no_off) + base.acres_parcel_mixed_no_off + + end_state.pop = feature.pop if feature.total_redev is True else \ + feature.pop - (feature.dev_pct * developable.pop) + base.pop + + end_state.hh = feature.hh if feature.total_redev is True else \ + feature.hh - (feature.dev_pct * developable.hh) + base.hh + + end_state.du = feature.du if feature.total_redev is True else \ + feature.du - (feature.dev_pct * developable.du) + base.du + + end_state.du_detsf_ll = feature.du_detsf_ll if feature.total_redev is True else \ + feature.du_detsf_ll - (feature.dev_pct * developable.du_detsf_ll) + base.du_detsf_ll + + end_state.du_detsf_sl = feature.du_detsf_sl if feature.total_redev is True else \ + feature.du_detsf_sl - (feature.dev_pct * developable.du_detsf_sl) + base.du_detsf_sl + + end_state.du_attsf = feature.du_attsf if feature.total_redev is True else \ + feature.du_attsf - (feature.dev_pct * developable.du_attsf) + base.du_attsf + + end_state.du_mf = feature.du_mf if feature.total_redev is True else \ + feature.du_mf - (feature.dev_pct * developable.du_mf) + base.du_mf + + end_state.du_mf2to4 = feature.du_mf2to4 if feature.total_redev is True else \ + feature.du_mf2to4 - (feature.dev_pct * developable.du_mf2to4) + base.du_mf2to4 + + end_state.du_mf5p = feature.du_mf5p if feature.total_redev is True else \ + feature.du_mf5p - (feature.dev_pct * developable.du_mf5p) + base.du_mf5p + + end_state.emp = feature.emp if feature.total_redev is True else \ + feature.emp - (feature.dev_pct * developable.emp) + base.emp + + end_state.emp_ret = feature.emp_ret if feature.total_redev is True else \ + feature.emp_ret - (feature.dev_pct * developable.emp_ret) + base.emp_ret + + end_state.emp_retail_services = feature.emp_retail_services if feature.total_redev is True else \ + feature.emp_retail_services - (feature.dev_pct * developable.emp_retail_services) + base.emp_retail_services + + end_state.emp_restaurant = feature.emp_restaurant if feature.total_redev is True else \ + feature.emp_restaurant - (feature.dev_pct * developable.emp_restaurant) + base.emp_restaurant + + end_state.emp_accommodation = feature.emp_accommodation if feature.total_redev is True else \ + feature.emp_accommodation - (feature.dev_pct * developable.emp_accommodation) + base.emp_accommodation + + end_state.emp_arts_entertainment = feature.emp_arts_entertainment if feature.total_redev is True else \ + feature.emp_arts_entertainment - (feature.dev_pct * developable.emp_arts_entertainment) + base.emp_arts_entertainment + + end_state.emp_other_services = feature.emp_other_services if feature.total_redev is True else \ + feature.emp_other_services - (feature.dev_pct * developable.emp_other_services) + base.emp_other_services + + end_state.emp_off = feature.emp_off if feature.total_redev is True else \ + feature.emp_off - (feature.dev_pct * developable.emp_off) + base.emp_off + + end_state.emp_office_services = feature.emp_office_services if feature.total_redev is True else \ + feature.emp_office_services - (feature.dev_pct * developable.emp_office_services) + base.emp_office_services + + end_state.emp_education = feature.emp_education if feature.total_redev is True else \ + feature.emp_education - (feature.dev_pct * developable.emp_education) + base.emp_education + + end_state.emp_public_admin = feature.emp_public_admin if feature.total_redev is True else \ + feature.emp_public_admin - (feature.dev_pct * developable.emp_public_admin) + base.emp_public_admin + + end_state.emp_medical_services = feature.emp_medical_services if feature.total_redev is True else \ + feature.emp_medical_services - (feature.dev_pct * developable.emp_medical_services) + base.emp_medical_services + + end_state.emp_ind = feature.emp_ind if feature.total_redev is True else \ + feature.emp_ind - (feature.dev_pct * developable.emp_ind) + base.emp_ind + + end_state.emp_wholesale = feature.emp_wholesale if feature.total_redev is True else \ + feature.emp_wholesale - (feature.dev_pct * developable.emp_wholesale) + base.emp_wholesale + + end_state.emp_transport_warehousing = feature.emp_transport_warehousing if feature.total_redev is True else \ + feature.emp_transport_warehousing - (feature.dev_pct * developable.emp_transport_warehousing) + base.emp_transport_warehousing + + end_state.emp_manufacturing = feature.emp_manufacturing if feature.total_redev is True else \ + feature.emp_manufacturing - (feature.dev_pct * developable.emp_manufacturing) + base.emp_manufacturing + + end_state.emp_construction_utilities = feature.emp_construction_utilities if feature.total_redev is True else \ + feature.emp_construction_utilities - ( + (feature.dev_pct * (developable.emp_construction + developable.emp_utilities))) + \ + (base.emp_construction + base.emp_utilities) + + end_state.emp_ag = feature.emp_ag if feature.total_redev is True else \ + feature.emp_ag - (feature.dev_pct * developable.emp_ag) + base.emp_ag + + end_state.emp_agriculture = feature.emp_agriculture if feature.total_redev is True else \ + feature.emp_agriculture - (feature.dev_pct * developable.emp_agriculture) + base.emp_agriculture + + end_state.emp_extraction = feature.emp_extraction if feature.total_redev is True else \ + feature.emp_extraction - (feature.dev_pct * developable.emp_extraction) + base.emp_extraction + + end_state.emp_military = feature.emp_military if feature.total_redev is True else \ + feature.emp_military - (feature.dev_pct * developable.emp_military) + base.emp_military + + end_state.bldg_sqft_detsf_ll = feature.bldg_sqft_detsf_ll if feature.total_redev is True else \ + feature.bldg_sqft_detsf_ll - (feature.dev_pct * developable.bldg_sqft_detsf_ll) + base.bldg_sqft_detsf_ll + + end_state.bldg_sqft_detsf_sl = feature.bldg_sqft_detsf_sl if feature.total_redev is True else \ + feature.bldg_sqft_detsf_sl - (feature.dev_pct * developable.bldg_sqft_detsf_sl) + base.bldg_sqft_detsf_sl + + end_state.bldg_sqft_attsf = feature.bldg_sqft_attsf if feature.total_redev is True else \ + feature.bldg_sqft_attsf - (feature.dev_pct * developable.bldg_sqft_attsf) + base.bldg_sqft_attsf + + end_state.bldg_sqft_mf = feature.bldg_sqft_mf if feature.total_redev is True else \ + feature.bldg_sqft_mf - (feature.dev_pct * developable.bldg_sqft_mf) + base.bldg_sqft_mf + + end_state.bldg_sqft_retail_services = feature.bldg_sqft_retail_services if feature.total_redev is True else \ + feature.bldg_sqft_retail_services - (feature.dev_pct * developable.bldg_sqft_retail_services) + base.bldg_sqft_retail_services + + end_state.bldg_sqft_restaurant = feature.bldg_sqft_restaurant if feature.total_redev is True else \ + feature.bldg_sqft_restaurant - (feature.dev_pct * developable.bldg_sqft_restaurant) + base.bldg_sqft_restaurant + + end_state.bldg_sqft_accommodation = feature.bldg_sqft_accommodation if feature.total_redev is True else \ + feature.bldg_sqft_accommodation - (feature.dev_pct * developable.bldg_sqft_accommodation) + base.bldg_sqft_accommodation + + end_state.bldg_sqft_arts_entertainment = feature.bldg_sqft_arts_entertainment if feature.total_redev is True else \ + feature.bldg_sqft_arts_entertainment - (feature.dev_pct * developable.bldg_sqft_arts_entertainment) + base.bldg_sqft_arts_entertainment + + end_state.bldg_sqft_other_services = feature.bldg_sqft_other_services if feature.total_redev is True else \ + feature.bldg_sqft_other_services - (feature.dev_pct * developable.bldg_sqft_other_services) + base.bldg_sqft_other_services + + end_state.bldg_sqft_office_services = feature.bldg_sqft_office_services if feature.total_redev is True else \ + feature.bldg_sqft_office_services - (feature.dev_pct * developable.bldg_sqft_office_services) + base.bldg_sqft_office_services + + end_state.bldg_sqft_public_admin = feature.bldg_sqft_public_admin if feature.total_redev is True else \ + feature.bldg_sqft_public_admin - (feature.dev_pct * developable.bldg_sqft_public_admin) + base.bldg_sqft_public_admin + + end_state.bldg_sqft_medical_services = feature.bldg_sqft_medical_services if feature.total_redev is True else \ + feature.bldg_sqft_medical_services - (feature.dev_pct * developable.bldg_sqft_medical_services) + base.bldg_sqft_medical_services + + end_state.bldg_sqft_education = feature.bldg_sqft_education if feature.total_redev is True else \ + feature.bldg_sqft_education - (feature.dev_pct * developable.bldg_sqft_education) + base.bldg_sqft_education + + end_state.bldg_sqft_wholesale = feature.bldg_sqft_wholesale if feature.total_redev is True else \ + feature.bldg_sqft_wholesale - (feature.dev_pct * developable.bldg_sqft_wholesale) + base.bldg_sqft_wholesale + + end_state.bldg_sqft_transport_warehousing = feature.bldg_sqft_transport_warehousing if feature.total_redev is True else \ + feature.bldg_sqft_transport_warehousing - (feature.dev_pct * developable.bldg_sqft_transport_warehousing) + base.bldg_sqft_transport_warehousing + + end_state.commercial_irrigated_sqft = feature.commercial_irrigated_sqft if feature.total_redev is True else \ + feature.commercial_irrigated_sqft - (feature.dev_pct * developable.commercial_irrigated_sqft) + base.commercial_irrigated_sqft + + end_state.residential_irrigated_sqft = feature.residential_irrigated_sqft if feature.total_redev is True else \ + feature.residential_irrigated_sqft - (feature.dev_pct * developable.residential_irrigated_sqft) + base.residential_irrigated_sqft + + end_state.save() + diff --git a/footprint/main/models/analysis_module/core_module/core_update_future_scenario_feature.py b/footprint/main/models/analysis_module/core_module/core_update_future_scenario_feature.py new file mode 100644 index 000000000..dfc02e270 --- /dev/null +++ b/footprint/main/models/analysis_module/core_module/core_update_future_scenario_feature.py @@ -0,0 +1,201 @@ +from footprint.main.lib.functions import map_to_dict +from footprint.main.models import FlatBuiltForm +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.query_parsing import annotated_related_feature_class_pk_via_geographies +from django.utils.timezone import utc +import datetime + +__author__ = 'calthorpe' + + +def update_future_scenario_feature(config_entity): + future_scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE) + developable_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_DEVELOPABLE) + + now = datetime.datetime.utcnow().replace(tzinfo=utc) + features = future_scenario_class.objects.filter( + updated__gt=now - datetime.timedelta(seconds=60)) + + annotated_features = annotated_related_feature_class_pk_via_geographies(features, config_entity, [Keys.DB_ABSTRACT_DEVELOPABLE]) + flat_built_forms = map_to_dict( + lambda flat_built_form: [flat_built_form.built_form_id, flat_built_form], + FlatBuiltForm.objects.filter(built_form_id__in=map(lambda feature: feature.built_form.id, filter(lambda feature: feature.built_form, features))) + ) + + for feature in annotated_features: + + developable = developable_class.objects.get(id=feature.developable) + flat_built_form = flat_built_forms.get(feature.built_form.id, FlatBuiltForm()) if feature.built_form else FlatBuiltForm() + + feature.dirty_flag = True + feature.refill_flag = True if developable.acres_urban > developable.acres_greenfield else False + + feature.acres_developing = developable.acres_developable + feature.acres_developable = developable.acres_developable * feature.dev_pct + + feature.intersection_density_sqmi = flat_built_form.intersection_density * feature.dev_pct + + feature.acres_parcel = (flat_built_form.acres_parcel_residential + flat_built_form.acres_parcel_employment + + flat_built_form.acres_parcel_mixed_use) * developable.acres_developable * feature.dev_pct + + + feature.acres_parcel_res = flat_built_form.acres_parcel_residential * developable.acres_developable * \ + feature.dev_pct if feature.total_redev is False else flat_built_form.acres_parcel_residential * \ + developable.acres_developable + + feature.acres_parcel_res_detsf_ll = flat_built_form.acres_parcel_residential_single_family_large_lot * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_residential_single_family_large_lot * developable.acres_developable + + feature.acres_parcel_res_detsf_sl = flat_built_form.acres_parcel_residential_single_family_small_lot * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_residential_single_family_small_lot * developable.acres_developable + + feature.acres_parcel_res_attsf = flat_built_form.acres_parcel_residential_attached_single_family * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_residential_attached_single_family * developable.acres_developable + + feature.acres_parcel_res_mf = flat_built_form.acres_parcel_residential_multifamily * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_residential_multifamily * developable.acres_developable + + feature.acres_parcel_emp = flat_built_form.acres_parcel_employment * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_employment * developable.acres_developable + + feature.acres_parcel_emp_ret = flat_built_form.acres_parcel_employment_retail * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_employment_retail * developable.acres_developable + + feature.acres_parcel_emp_off = flat_built_form.acres_parcel_employment_office * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_employment_office * developable.acres_developable + + feature.acres_parcel_emp_ind = flat_built_form.acres_parcel_employment_industrial * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_employment_industrial * developable.acres_developable + + feature.acres_parcel_emp_ag = flat_built_form.acres_parcel_employment_agriculture * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_employment_agriculture * developable.acres_developable + + feature.acres_parcel_emp_mixed = flat_built_form.acres_parcel_employment_mixed * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_employment_mixed * developable.acres_developable + + feature.acres_parcel_mixed = flat_built_form.acres_parcel_mixed_use * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_mixed_use * developable.acres_developable + + feature.acres_parcel_mixed_w_off = flat_built_form.acres_parcel_mixed_use_with_office * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_mixed_use_with_office * developable.acres_developable + + feature.acres_parcel_mixed_no_off = flat_built_form.acres_parcel_mixed_use_without_office * \ + developable.acres_developable * feature.dev_pct if feature.total_redev is False \ + else flat_built_form.acres_parcel_mixed_use_without_office * developable.acres_developable + + feature.pop = flat_built_form.population_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.hh = flat_built_form.household_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.du = flat_built_form.dwelling_unit_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.du_detsf_ll = flat_built_form.single_family_large_lot_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.du_detsf_sl = flat_built_form.single_family_small_lot_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.du_attsf = flat_built_form.attached_single_family_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.du_mf = (flat_built_form.multifamily_2_to_4_density + flat_built_form.multifamily_5_plus_density) \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + + feature.du_mf2to4 = flat_built_form.multifamily_2_to_4_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.du_mf5p = flat_built_form.multifamily_5_plus_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + + + feature.emp = flat_built_form.employment_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_ret = flat_built_form.retail_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_retail_services = flat_built_form.retail_services_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_restaurant = flat_built_form.residential_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_accommodation = flat_built_form.accommodation_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_arts_entertainment = flat_built_form.arts_entertainment_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_other_services = flat_built_form.other_services_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + + feature.emp_off = flat_built_form.office_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_office_services = flat_built_form.office_services_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_education = flat_built_form.education_services_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_public_admin = flat_built_form.public_admin_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_medical_services = flat_built_form.medical_services_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + + feature.emp_ind = flat_built_form.industrial_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_wholesale = flat_built_form.wholesale_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_transport_warehousing = flat_built_form.transport_warehouse_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_manufacturing = flat_built_form.manufacturing_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_construction_utilities = flat_built_form.construction_utilities_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + + feature.emp_ag = flat_built_form.agricultural_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_agriculture = flat_built_form.agriculture_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_extraction = flat_built_form.extraction_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.emp_military = flat_built_form.armed_forces_density \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_detsf_ll = flat_built_form.building_sqft_single_family_large_lot \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_detsf_sl = flat_built_form.building_sqft_single_family_small_lot \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_attsf = flat_built_form.building_sqft_attached_single_family \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_mf = ( + flat_built_form.building_sqft_multifamily_2_to_4 + flat_built_form.building_sqft_multifamily_5_plus) * developable.acres_developable \ + * feature.dev_pct * feature.density_pct + feature.bldg_sqft_retail_services = flat_built_form.building_sqft_retail_services \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_restaurant = flat_built_form.building_sqft_restaurant \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_accommodation = flat_built_form.building_sqft_accommodation \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_arts_entertainment = flat_built_form.building_sqft_arts_entertainment \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_other_services = flat_built_form.building_sqft_other_services \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_office_services = flat_built_form.building_sqft_office_services \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_public_admin = flat_built_form.building_sqft_public_admin \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_medical_services = flat_built_form.building_sqft_medical_services \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_education = flat_built_form.building_sqft_education_services \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_wholesale = flat_built_form.building_sqft_wholesale \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.bldg_sqft_transport_warehousing = flat_built_form.building_sqft_transport_warehouse \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + + feature.commercial_irrigated_sqft = flat_built_form.commercial_irrigated_square_feet \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + feature.residential_irrigated_sqft = flat_built_form.residential_irrigated_square_feet \ + * developable.acres_developable * feature.dev_pct * feature.density_pct + + feature.save() diff --git a/footprint/main/models/analysis_module/core_module/core_update_increment_feature.py b/footprint/main/models/analysis_module/core_module/core_update_increment_feature.py new file mode 100644 index 000000000..fb87264c9 --- /dev/null +++ b/footprint/main/models/analysis_module/core_module/core_update_increment_feature.py @@ -0,0 +1,104 @@ +from footprint.main.models import FlatBuiltForm +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.query_parsing import annotated_related_feature_class_pk_via_geographies + +__author__ = 'calthorpe' + + +def update_increment_feature(config_entity): + future_scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE) + end_state_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_FEATURE) + base_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_FEATURE) + increment_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_INCREMENT_FEATURE) + developable_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_DEVELOPABLE) + + features = future_scenario_class.objects.filter(dirty_flag=True) + + + annotated_features = annotated_related_feature_class_pk_via_geographies(features, config_entity, [Keys.DB_ABSTRACT_DEVELOPABLE, Keys.DB_ABSTRACT_BASE_FEATURE, Keys.DB_ABSTRACT_END_STATE_FEATURE, Keys.DB_ABSTRACT_INCREMENT_FEATURE]) + + for feature in annotated_features: + built_form = FlatBuiltForm.objects.get(built_form_id=feature.built_form.id) \ + if feature.built_form \ + else FlatBuiltForm() + + increment = increment_class.objects.get(id=feature.increments) + end_state = end_state_class.objects.get(id=feature.end_state) + base = base_class.objects.get(id=feature.base_feature) + developable = developable_class.objects.get(id=feature.developable) + + increment.land_development_category = calculate_land_development_category(base, built_form) + + if developable.acres_greenfield < developable.acres_urban: + increment.refill_flag = 1 + else: + increment.refill_flag = None + + increment.pop = end_state.pop - base.pop + increment.hh = end_state.hh - base.hh + increment.du = end_state.du - base.du + increment.emp = end_state.emp - base.emp + increment.du_detsf = end_state.du_detsf - base.du_detsf + increment.du_detsf_ll = end_state.du_detsf_ll - base.du_detsf_ll + increment.du_detsf_sl = end_state.du_detsf_sl - base.du_detsf_sl + increment.du_attsf = end_state.du_attsf - base.du_attsf + increment.du_mf = end_state.du_mf - base.du_mf + + increment.emp_ret = end_state.emp_ret - base.emp_ret + increment.emp_retail_services = end_state.emp_retail_services - base.emp_retail_services + increment.emp_restaurant = end_state.emp_restaurant - base.emp_restaurant + increment.emp_accommodation = end_state.emp_accommodation - base.emp_accommodation + increment.emp_arts_entertainment = end_state.emp_arts_entertainment - base.emp_arts_entertainment + increment.emp_other_services = end_state.emp_other_services - base.emp_other_services + + increment.emp_off = end_state.emp_off - base.emp_off + increment.emp_office_services = end_state.emp_office_services - base.emp_office_services + increment.emp_education = end_state.emp_education - base.emp_education + increment.emp_public_admin = end_state.emp_public_admin - base.emp_public_admin + increment.emp_medical_services = end_state.emp_medical_services - base.emp_medical_services + + increment.emp_ind = end_state.emp_ind - base.emp_ind + increment.emp_wholesale = end_state.emp_wholesale - base.emp_wholesale + increment.emp_transport_warehousing = end_state.emp_transport_warehousing - base.emp_transport_warehousing + increment.emp_manufacturing = end_state.emp_manufacturing - base.emp_manufacturing + increment.emp_utilities = end_state.emp_utilities - base.emp_utilities + increment.emp_construction = end_state.emp_construction - base.emp_construction + + increment.emp_ag = end_state.emp_ag - base.emp_ag + increment.emp_agriculture = end_state.emp_agriculture - base.emp_agriculture + increment.emp_extraction = end_state.emp_extraction - base.emp_extraction + + increment.emp_military = end_state.emp_military - base.emp_military + + feature.dirty_flag = False + + increment.save() + feature.save() + + +def calculate_land_development_category(base, built_form): + + try: + + if built_form.intersection_density >= 150 and built_form.employment_density > 70: + land_development_category = 'urban' + + elif built_form.intersection_density >= 150 and built_form.dwelling_unit_density > 45: + land_development_category = 'urban' + + elif built_form.intersection_density >= 150 and built_form.employment_density <= 70: + land_development_category = 'compact' + + elif built_form.intersection_density >= 150 and built_form.dwelling_unit_density <= 45: + land_development_category = 'compact' + + elif built_form.intersection_density < 150: + land_development_category = 'standard' + + else: + land_development_category = None + except: + raise Exception + print 'failure to calculate land development category' + return land_development_category + diff --git a/footprint/main/models/analysis_module/fiscal_module/__init__.py b/footprint/main/models/analysis_module/fiscal_module/__init__.py new file mode 100644 index 000000000..4f1f5a862 --- /dev/null +++ b/footprint/main/models/analysis_module/fiscal_module/__init__.py @@ -0,0 +1 @@ +__author__ = 'calthorpe_associates' diff --git a/footprint/main/models/analysis_module/fiscal_module/fiscal.py b/footprint/main/models/analysis_module/fiscal_module/fiscal.py new file mode 100644 index 000000000..1b2d66ce2 --- /dev/null +++ b/footprint/main/models/analysis_module/fiscal_module/fiscal.py @@ -0,0 +1,253 @@ +import time +from footprint.celery import app +from footprint.common.utils.websockets import send_message_to_client +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.tasks.async_job import Job +from footprint.main.models.analysis_module.analysis_module import AnalysisModule +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +import logging +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +logger = logging.getLogger(__name__) + +class Fiscal(AnalysisModule): + + objects = GeoInheritanceManager() + + def start(self): + job = Job.objects.create( + type="fiscal", + status="New", + user=self.user + ) + job.save() + + task = executeFiscal.apply_async( + args=[job.hashid, self.user, self.config_entity], + soft_time_limit=3600, + time_limit=3600, + countdown=1 + ) + job = Job.objects.get(hashid=job.hashid) + job.task_id = task.id + + class Meta(object): + app_label = 'main' + + +@app.task +def executeFiscal(hash_id, user, config_entity): + # Make sure all related models have been created before querying + FeatureClassCreator(config_entity).ensure_dynamic_models() + logger.info("Executing Fiscal using {0}".format(config_entity)) + run_fiscal_calculations(config_entity) + logger.info("Done executing Fiscal") + logger.info("Executed Fiscal using {0}".format(config_entity)) + send_message_to_client(user.id, + dict(event='FiscalModelCompleted', + config_entity_id=config_entity.id) + ) + +def run_fiscal_calculations(config_entity): + start_time = time.time() + + policy_assumptions = {} + + #Operations and Maintenance assumptions passed from the active scenario policy set + policy_assumptions['urban_operations_maintenance_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.urban.single_family_large_lot')) + policy_assumptions['urban_operations_maintenance_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.urban.single_family_small_lot')) + policy_assumptions['urban_operations_maintenance_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.urban.single_family_attached')) + policy_assumptions['urban_operations_maintenance_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.urban.multifamily')) + + policy_assumptions['compact_refill_operations_maintenance_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.compact_refill.single_family_large_lot')) + policy_assumptions['compact_refill_operations_maintenance_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.compact_refill.single_family_small_lot')) + policy_assumptions['compact_refill_operations_maintenance_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.compact_refill.single_family_attached')) + policy_assumptions['compact_refill_operations_maintenance_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.compact_refill.multifamily')) + + policy_assumptions['compact_greenfield_operations_maintenance_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.compact_greenfield.single_family_large_lot')) + policy_assumptions['compact_greenfield_operations_maintenance_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.compact_greenfield.single_family_small_lot')) + policy_assumptions['compact_greenfield_operations_maintenance_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.compact_greenfield.single_family_attached')) + policy_assumptions['compact_greenfield_operations_maintenance_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.compact_greenfield.multifamily')) + + policy_assumptions['standard_operations_maintenance_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.standard.single_family_large_lot')) + policy_assumptions['standard_operations_maintenance_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.standard.single_family_small_lot')) + policy_assumptions['standard_operations_maintenance_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.standard.single_family_attached')) + policy_assumptions['standard_operations_maintenance_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.operations_maintenance_costs.standard.multifamily')) + + #Revenue assumptions passed from the active scenario policy set + policy_assumptions['urban_revenue_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.urban.single_family_large_lot')) + policy_assumptions['urban_revenue_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.urban.single_family_small_lot')) + policy_assumptions['urban_revenue_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.urban.single_family_attached')) + policy_assumptions['urban_revenue_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.urban.multifamily')) + + policy_assumptions['compact_refill_revenue_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.compact_refill.single_family_large_lot')) + policy_assumptions['compact_refill_revenue_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.compact_refill.single_family_small_lot')) + policy_assumptions['compact_refill_revenue_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.compact_refill.single_family_attached')) + policy_assumptions['compact_refill_revenue_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.compact_refill.multifamily')) + + policy_assumptions['compact_greenfield_revenue_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.compact_greenfield.single_family_large_lot')) + policy_assumptions['compact_greenfield_revenue_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.compact_greenfield.single_family_small_lot')) + policy_assumptions['compact_greenfield_revenue_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.compact_greenfield.single_family_attached')) + policy_assumptions['compact_greenfield_revenue_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.compact_greenfield.multifamily')) + + policy_assumptions['standard_revenue_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.standard.single_family_large_lot')) + policy_assumptions['standard_revenue_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.standard.single_family_small_lot')) + policy_assumptions['standard_revenue_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.standard.single_family_attached')) + policy_assumptions['standard_revenue_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.revenue.standard.multifamily')) + + #Capital Cost assumptions passed from the active scenario policy set + policy_assumptions['urban_capital_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.urban.single_family_large_lot')) + policy_assumptions['urban_capital_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.urban.single_family_small_lot')) + policy_assumptions['urban_capital_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.urban.single_family_attached')) + policy_assumptions['urban_capital_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.urban.multifamily')) + + policy_assumptions['compact_refill_capital_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.compact_refill.single_family_large_lot')) + policy_assumptions['compact_refill_capital_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.compact_refill.single_family_small_lot')) + policy_assumptions['compact_refill_capital_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.compact_refill.single_family_attached')) + policy_assumptions['compact_refill_capital_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.compact_refill.multifamily')) + + policy_assumptions['compact_greenfield_capital_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.compact_greenfield.single_family_large_lot')) + policy_assumptions['compact_greenfield_capital_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.compact_greenfield.single_family_small_lot')) + policy_assumptions['compact_greenfield_capital_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.compact_greenfield.single_family_attached')) + policy_assumptions['compact_greenfield_capital_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.compact_greenfield.multifamily')) + + policy_assumptions['standard_capital_sfll'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.standard.single_family_large_lot')) + policy_assumptions['standard_capital_sfsl'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.standard.single_family_small_lot')) + policy_assumptions['standard_capital_attsf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.standard.single_family_attached')) + policy_assumptions['standard_capital_mf'] = float(config_entity.selected_policy_set().\ + policy_by_key('fiscal.capital_costs.standard.multifamily')) + + scenario_time_increment = float(config_entity.year - config_entity.project.base_year) + fiscal_feature_class = config_entity.db_entity_feature_class(Keys.DB_ABSTRACT_FISCAL_FEATURE, base_class=True) + net_increment_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_INCREMENT_FEATURE) + + features = net_increment_class.objects.filter(land_development_category__isnull = False) + + fiscal_outputs = [] + + for feature in features: + new_feature = fiscal_feature_class() + new_feature.id = feature.id + new_feature.residential_capital_costs = calculate_residential_capital_costs(feature, policy_assumptions) + new_feature.residential_operations_maintenance_costs = calculate_residential_operations_maintenance_costs\ + (feature, policy_assumptions, scenario_time_increment) + new_feature.residential_revenue = calculate_residential_revenue(feature, policy_assumptions) + new_feature.wkb_geometry = feature.wkb_geometry + + fiscal_outputs.append(new_feature) + + fiscal_feature_class.objects.all().delete() + fiscal_feature_class.objects.bulk_create(fiscal_outputs) + + print 'Finished: ' + str(time.time() - start_time) + + from footprint.main.publishing.config_entity_publishing import post_save_config_entity_analytic_run + post_save_config_entity_analytic_run.send(sender=config_entity.__class__, config_entity=config_entity, module='fiscal') + + +def calculate_residential_capital_costs(feature, policy_assumptions): + + if feature.land_development_category == 'urban': + residential_capital_costs = (float(feature.du_detsf_ll) * policy_assumptions['urban_capital_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['urban_capital_sfsl']) + (float(feature.du_attsf) * policy_assumptions['urban_capital_attsf']) + (float(feature.du_mf) * policy_assumptions['urban_capital_mf']) + return residential_capital_costs + + elif feature.land_development_category == 'compact' and feature.refill_flag == True: + residential_capital_costs = (float(feature.du_detsf_ll) * policy_assumptions['compact_refill_capital_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['compact_refill_capital_sfsl']) + (float(feature.du_attsf) * policy_assumptions['compact_refill_capital_attsf']) + (float(feature.du_mf) * policy_assumptions['compact_refill_capital_mf']) + return residential_capital_costs + + elif feature.land_development_category == 'compact' and feature.refill_flag == False: + residential_capital_costs = float((feature.du_detsf_ll) * policy_assumptions['compact_greenfield_capital_sfll']) + float((feature.du_detsf_sl) * policy_assumptions['compact_greenfield_capital_sfsl']) + float((feature.du_attsf) * policy_assumptions['compact_greenfield_capital_attsf']) + float((feature.du_mf) * policy_assumptions['compact_greenfield_capital_mf']) + return residential_capital_costs + + elif feature.land_development_category == 'standard': + residential_capital_costs = (float(feature.du_detsf_ll) * policy_assumptions['standard_capital_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['standard_capital_sfsl']) + (float(feature.du_attsf) * policy_assumptions['standard_capital_attsf']) + (float(feature.du_mf) * policy_assumptions['standard_capital_mf']) + + return residential_capital_costs + + + +def calculate_residential_operations_maintenance_costs(feature, policy_assumptions, scenario_time_increment): + + if feature.land_development_category == 'urban': + residential_operation_maintenance_costs = ((float(feature.du_detsf_ll) * policy_assumptions['urban_operations_maintenance_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['urban_operations_maintenance_sfsl']) + (float(feature.du_attsf) * policy_assumptions['urban_operations_maintenance_attsf']) + (float(feature.du_mf) * policy_assumptions['urban_operations_maintenance_mf'])) * scenario_time_increment + return residential_operation_maintenance_costs + + elif feature.land_development_category == 'compact' and feature.refill_flag == True: + residential_operation_maintenance_costs = ((float(feature.du_detsf_ll) * policy_assumptions['compact_refill_operations_maintenance_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['compact_refill_operations_maintenance_sfsl']) + (float(feature.du_attsf) * policy_assumptions['compact_refill_operations_maintenance_attsf']) + (float(feature.du_mf) * policy_assumptions['compact_refill_operations_maintenance_mf'])) * scenario_time_increment + return residential_operation_maintenance_costs + + elif feature.land_development_category == 'compact' and feature.refill_flag == False: + residential_operation_maintenance_costs = ((float(feature.du_detsf_ll) * policy_assumptions['compact_greenfield_operations_maintenance_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['compact_greenfield_operations_maintenance_sfsl']) + (float(feature.du_attsf) * policy_assumptions['compact_greenfield_operations_maintenance_attsf']) + (float(feature.du_mf) * policy_assumptions['compact_greenfield_operations_maintenance_mf'])) * scenario_time_increment + return residential_operation_maintenance_costs + + elif feature.land_development_category == 'standard': + residential_operation_maintenance_costs = ((float(feature.du_detsf_ll) * policy_assumptions['standard_operations_maintenance_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['standard_operations_maintenance_sfsl']) + (float(feature.du_attsf) * policy_assumptions['standard_operations_maintenance_attsf']) + (float(feature.du_mf) * policy_assumptions['standard_operations_maintenance_mf'])) * scenario_time_increment + return residential_operation_maintenance_costs + + + +def calculate_residential_revenue(feature, policy_assumptions): + + if feature.land_development_category == 'urban': + residential_revenue_costs = (float(feature.du_detsf_ll) * policy_assumptions['urban_revenue_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['urban_revenue_sfsl']) + (float(feature.du_attsf) * policy_assumptions['urban_revenue_attsf']) + (float(feature.du_mf) * policy_assumptions['urban_revenue_mf']) + + elif feature.land_development_category == 'compact' and feature.refill_flag == True: + residential_revenue_costs = (float(feature.du_detsf_ll) * policy_assumptions['compact_refill_revenue_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['compact_refill_revenue_sfsl']) + (float(feature.du_attsf) * policy_assumptions['compact_refill_revenue_attsf']) + (float(feature.du_mf) * policy_assumptions['compact_refill_revenue_mf']) + + elif feature.land_development_category == 'compact' and feature.refill_flag == False: + residential_revenue_costs = (float(feature.du_detsf_ll) * policy_assumptions['compact_greenfield_revenue_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['compact_greenfield_revenue_sfsl']) + (float(feature.du_attsf) * policy_assumptions['compact_greenfield_revenue_attsf']) + (float(feature.du_mf) * policy_assumptions['compact_greenfield_revenue_mf']) + + elif feature.land_development_category == 'standard': + residential_revenue_costs = (float(feature.du_detsf_ll) * policy_assumptions['standard_revenue_sfll']) + (float(feature.du_detsf_sl) * policy_assumptions['standard_revenue_sfsl']) + (float(feature.du_attsf) * policy_assumptions['standard_revenue_attsf']) + (float(feature.du_mf) * policy_assumptions['standard_revenue_mf']) + + return residential_revenue_costs \ No newline at end of file diff --git a/footprint/main/models/analysis_module/vmt_module/__init__.py b/footprint/main/models/analysis_module/vmt_module/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/analysis_module/vmt_module/vmt.py b/footprint/main/models/analysis_module/vmt_module/vmt.py new file mode 100644 index 000000000..143825b8d --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt.py @@ -0,0 +1,240 @@ +from footprint.main.models.config.scenario import FutureScenario + +__author__ = 'calthorpe' + +from django.db.models import Sum +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.query_parsing import annotated_related_feature_class_pk_via_geographies +from footprint.main.utils.utils import parse_schema_and_table +from vmt_calculate_final_results import calculate_final_vmt_results +from vmt_calculate_log_odds import calculate_log_odds +from vmt_model_constants import vmt_output_field_list +from vmt_raw_trip_generation import generate_raw_trips +from vmt_trip_purpose_splits import calculate_trip_purpose_splits +from vmt_write_results_to_database import write_vmt_results_to_database +from footprint.celery import app +from footprint.common.utils.websockets import send_message_to_client +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.analysis_module.vmt_module.vmt_variable_buffer_preprocess import run_variable_buffers +from footprint.main.models.tasks.async_job import Job +from footprint.main.models.analysis_module.analysis_module import AnalysisModule +from footprint.main.models.analysis_module.vmt_module.vmt_quarter_mile_buffer_preprocess import run_quarter_mile_buffers +from footprint.main.models.analysis_module.vmt_module.vmt_one_mile_buffer_preprocess import run_one_mile_buffers +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +import logging + +logger = logging.getLogger(__name__) + +class Vmt(AnalysisModule): + + objects = GeoInheritanceManager() + + def start(self): + + job = Job.objects.create( + type="vmt", + status="New", + user=self.user + ) + job.save() + + task = executeVmt.apply_async( + args=[job.hashid, self.user, self.config_entity], + soft_time_limit=3600, + time_limit=3600, + countdown=1 + ) + job = Job.objects.get(hashid=job.hashid) + job.task_id = task.id + + class Meta(object): + app_label = 'main' + + +@app.task +def executeVmt(hash_id, user, config_entity): + # Make sure all related models have been created before querying + logger.info("Executing Vmt using {0}".format(config_entity)) + run_quarter_mile_buffers(config_entity) + run_one_mile_buffers(config_entity) + run_variable_buffers(config_entity) + FeatureClassCreator(config_entity).ensure_dynamic_models() + run_vmt_model(config_entity) + + logger.info("Done executing Vmt") + logger.info("Executed Vmt using {0}".format(config_entity)) + send_message_to_client(user.id, + dict(event='VmtModelCompleted', + config_entity_id=config_entity.id) + ) + + + + +def run_vmt_model(config_entity): + + vmt_feature = {} + + one_mile_buffer_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_VMT_ONE_MILE_BUFFER_FEATURE) + quarter_mile_buffer_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_VMT_QUARTER_MILE_BUFFER_FEATURE) + variable_buffer_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_VMT_VARIABLE_BUFFER_FEATURE) + vmt_result_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_VMT_FEATURE) + + if isinstance(config_entity.subclassed_config_entity, FutureScenario): + scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_FEATURE) + demographic_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_DEMOGRAPHIC_FEATURE) + features = scenario_class.objects.all() + + annotated_features = annotated_related_feature_class_pk_via_geographies(features, config_entity, [ + Keys.DB_ABSTRACT_VMT_ONE_MILE_BUFFER_FEATURE, Keys.DB_ABSTRACT_VMT_QUARTER_MILE_BUFFER_FEATURE, + Keys.DB_ABSTRACT_VMT_VARIABLE_BUFFER_FEATURE, Keys.DB_ABSTRACT_END_STATE_DEMOGRAPHIC_FEATURE]) + future = True + + else: + scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_FEATURE) + demographic_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_DEMOGRAPHIC_FEATURE) + + features = scenario_class.objects.all() + + annotated_features = annotated_related_feature_class_pk_via_geographies(features, config_entity, [ + Keys.DB_ABSTRACT_VMT_ONE_MILE_BUFFER_FEATURE, Keys.DB_ABSTRACT_VMT_QUARTER_MILE_BUFFER_FEATURE, + Keys.DB_ABSTRACT_VMT_VARIABLE_BUFFER_FEATURE, Keys.DB_ABSTRACT_BASE_DEMOGRAPHIC_FEATURE]) + future = False + + options = dict( + vmt_result_table=vmt_result_class.db_entity_key, + vmt_schema=parse_schema_and_table(vmt_result_class._meta.db_table)[0], + input_table=scenario_class.db_entity_key, + input_schema=parse_schema_and_table(scenario_class._meta.db_table)[0] + ) + + total_employment = scenario_class.objects.aggregate(Sum('emp')) + vmt_output_list = [] + + for feature in annotated_features: + if future: + demographic = demographic_class.objects.get(id=feature.end_state_demographic_feature) + else: + demographic = demographic_class.objects.get(id=feature.base_demographic_feature) + + one_mile_buffer = one_mile_buffer_class.objects.get(id=feature.vmt_one_mile_buffer_feature) + quarter_mile_buffer = quarter_mile_buffer_class.objects.get(id=feature.vmt_quarter_mile_buffer_feature) + variable_buffer = variable_buffer_class.objects.get(id=feature.vmt_variable_buffer_feature) + + vmt_feature['id'] = int(feature.id) + vmt_feature['acres_gross'] = float(feature.acres_gross) + vmt_feature['acres_parcel'] = float(feature.acres_parcel) + vmt_feature['acres_parcel_res'] = float(feature.acres_parcel_res) + vmt_feature['acres_parcel_emp'] = float(feature.acres_parcel_emp) + vmt_feature['acres_parcel_mixed'] = float(feature.acres_parcel_mixed) + vmt_feature['intersections_qtrmi'] = float(feature.intersection_density_sqmi) + vmt_feature['du'] = float(feature.du) + vmt_feature['du_occupancy_rate'] = float(demographic.du_occupancy_rate) + vmt_feature['du_detsf'] = float(feature.du_detsf) + vmt_feature['du_attsf'] = float(feature.du_attsf) + + vmt_feature['du_mf'] = float(feature.du_mf) + vmt_feature['du_mf2to4'] = float(feature.du_mf2to4) + vmt_feature['du_mf5p'] = float(feature.du_mf5p) + vmt_feature['hh'] = float(feature.hh) + vmt_feature['hh_avg_size'] = float(demographic.hh_avg_size) + vmt_feature['hh_avg_inc'] = float(demographic.hh_avg_inc) + + vmt_feature['hh_inc_00_10'] = float(demographic.hh_inc_00_10) + vmt_feature['hh_inc_10_20'] = float(demographic.hh_inc_10_20) + vmt_feature['hh_inc_20_30'] = float(demographic.hh_inc_20_30) + vmt_feature['hh_inc_30_40'] = float(demographic.hh_inc_30_40) + vmt_feature['hh_inc_40_50'] = float(demographic.hh_inc_40_50) + vmt_feature['hh_inc_50_60'] = float(demographic.hh_inc_50_60) + vmt_feature['hh_inc_60_75'] = float(demographic.hh_inc_60_75) + vmt_feature['hh_inc_75_100'] = float(demographic.hh_inc_75_100) + vmt_feature['hh_inc_100p'] = float(demographic.hh_inc_100_125 + demographic.hh_inc_125_150 + \ + demographic.hh_inc_150_200 + demographic.hh_inc_200p) + + vmt_feature['pop'] = float(feature.pop) + vmt_feature['pop_employed'] = float(demographic.pop_employed) + vmt_feature['pop_age16_up'] = float(demographic.pop_age16_up) + vmt_feature['pop_age65_up'] = float(demographic.pop_age65_up) + + vmt_feature['emp'] = float(feature.emp) + vmt_feature['emp_retail'] = float(feature.emp_retail_services + feature.emp_other_services) + vmt_feature['emp_restaccom'] = float(feature.emp_accommodation + feature.emp_restaurant) + vmt_feature['emp_arts_entertainment'] = float(feature.emp_arts_entertainment) + vmt_feature['emp_office'] = float(feature.emp_office_services) + vmt_feature['emp_public'] = float(feature.emp_public_admin + feature.emp_education) + vmt_feature['emp_industry'] = float(feature.emp_ind + feature.emp_ag) + + vmt_feature['emp_within_1mile'] = float(one_mile_buffer.emp) + vmt_feature['hh_within_quarter_mile_trans'] = float(0) + + vmt_feature['vb_acres_parcel_res_total'] = float(variable_buffer.acres_parcel_res) + vmt_feature['vb_acres_parcel_emp_total'] = float(variable_buffer.acres_parcel_emp) + vmt_feature['vb_acres_parcel_mixed_total'] = float(variable_buffer.acres_parcel_mixed) + vmt_feature['vb_du_total'] = float(variable_buffer.du) + vmt_feature['vb_pop_total'] = float(variable_buffer.pop) + vmt_feature['vb_emp_total'] = float(variable_buffer.emp) + vmt_feature['vb_emp_retail_total'] = float(variable_buffer.emp_ret) + vmt_feature['vb_hh_total'] = float(variable_buffer.hh) + vmt_feature['vb_du_mf_total'] = float(variable_buffer.du_mf) + vmt_feature['vb_hh_inc_00_10_total'] = float(variable_buffer.hh_inc_00_10) + vmt_feature['vb_hh_inc_10_20_total'] = float(variable_buffer.hh_inc_10_20) + vmt_feature['vb_hh_inc_20_30_total'] = float(variable_buffer.hh_inc_20_30) + vmt_feature['vb_hh_inc_30_40_total'] = float(variable_buffer.hh_inc_30_40) + vmt_feature['vb_hh_inc_40_50_total'] = float(variable_buffer.hh_inc_40_50) + vmt_feature['vb_hh_inc_50_60_total'] = float(variable_buffer.hh_inc_50_60) + vmt_feature['vb_hh_inc_60_75_total'] = float(variable_buffer.hh_inc_60_75) + vmt_feature['vb_hh_inc_75_100_total'] = float(variable_buffer.hh_inc_75_100) + vmt_feature['vb_hh_inc_100p_total'] = float(variable_buffer.hh_inc_100p) + + vmt_feature['vb_pop_employed_total'] = float(variable_buffer.pop_employed) + vmt_feature['vb_pop_age16_up_total'] = float(variable_buffer.pop_age16_up) + vmt_feature['vb_pop_age65_up_total'] = float(variable_buffer.pop_age65_up) + + vmt_feature['emp30m_transit'] = float(variable_buffer.emp_30min_transit) + vmt_feature['emp45m_transit'] = float(variable_buffer.emp_45min_transit) + vmt_feature['prod_hbw'] = float(variable_buffer.productions_hbw) + vmt_feature['prod_hbo'] = float(variable_buffer.productions_hbo) + vmt_feature['prod_nhb'] = float(variable_buffer.productions_nhb) + vmt_feature['attr_hbw'] = float(variable_buffer.attractions_hbw) + vmt_feature['attr_hbo'] = float(variable_buffer.attractions_hbo) + vmt_feature['attr_nhb'] = float(variable_buffer.attractions_nhb) + + vmt_feature['qmb_acres_parcel_res_total'] = float(quarter_mile_buffer.acres_parcel_res) + vmt_feature['qmb_acres_parcel_emp_total'] = float(quarter_mile_buffer.acres_parcel_emp) + vmt_feature['qmb_acres_parcel_mixed_total'] = float(quarter_mile_buffer.acres_parcel_mixed) + vmt_feature['qmb_du_total'] = float(quarter_mile_buffer.du) + vmt_feature['qmb_pop_total'] = float(quarter_mile_buffer.pop) + vmt_feature['qmb_emp_total'] = float(quarter_mile_buffer.emp) + vmt_feature['qmb_emp_retail'] = float(quarter_mile_buffer.emp_ret) + vmt_feature['hh_avg_veh'] = float(demographic.hh_avg_vehicles) + + vmt_feature['truck_adjustment_factor'] = 0.031 + vmt_feature['total_employment'] = float(total_employment['emp__sum']) + #---------------------------------- + #run raw trip generation + #---------------------------------- + vmt_feature_trips = generate_raw_trips(vmt_feature) + #---------------------------------- + #run trip purpose splits + #---------------------------------- + vmt_feature_trip_purposes = calculate_trip_purpose_splits(vmt_feature_trips) + #---------------------------------- + #run log odds + #---------------------------------- + vmt_feature_log_odds = calculate_log_odds(vmt_feature_trip_purposes) + #---------------------------------- + #run vmt equations + #---------------------------------- + vmt_output = calculate_final_vmt_results(vmt_feature_log_odds) + + #filters the vmt feature dictionary for specific output fields for writing to the database + output_list = map(lambda key: vmt_output[key], vmt_output_field_list) + + vmt_output_list.append(output_list) + + #-- write result db table + write_vmt_results_to_database(options, vmt_output_list) + + + + diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_auto_ownership_model.py b/footprint/main/models/analysis_module/vmt_module/vmt_auto_ownership_model.py new file mode 100644 index 000000000..e9a966f5a --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_auto_ownership_model.py @@ -0,0 +1,303 @@ +import math +from footprint.main.models.analysis_module.vmt_module.vmt_model_constants import cAuto_const, cAuto_const_coef, cAuto_workers_per_hh_coef, cAuto_non_workers_16_64_hh_coef, cAuto_non_workers_65_hh_coef, cAuto_hh_income_coef, cAuto_hh_income_gt_100k_coef, cAuto_hh_mfdu_coef, cAuto_log_net_hu_dens_coef, cAuto_log_net_retail_dens_coef, cAuto_jobs_45_trans_coef + +__author__ = 'calthorpe' + + +def generate_predicted_auto_ownership(vmt_feature): + tRaw_trips_residential = vmt_feature['raw_trips_sf'] + vmt_feature['raw_trips_mf24'] + vmt_feature['raw_trips_mf5'] + tRaw_trips_nonresidential = vmt_feature['raw_trips_retail'] + vmt_feature['raw_trips_restaurant'] + vmt_feature[ + 'raw_trips_entertainment'] + \ + vmt_feature['raw_trips_office'] + vmt_feature['raw_trips_public'] + vmt_feature[ + 'raw_trips_industry'] + \ + vmt_feature['raw_trips_k12'] + vmt_feature['raw_trips_h_ed'] + + if vmt_feature['vb_du_total'] == 0 or vmt_feature['vb_du_total'] is None \ + or vmt_feature['hh'] == 0 or vmt_feature['hh'] is None: + tWorkers_per_HH = 0 + tNonWorkers_16_64_per_HH = 0 + tNonWorkers_65_per_HH = 0 + tMFU = 0 + + else: + if tRaw_trips_residential == 0 or tRaw_trips_nonresidential == 0: + tWorkers_per_HH = 0 + tNonWorkers_16_64_per_HH = 0 + tNonWorkers_65_per_HH = 0 + tMFU = 0 + else: + tAtmp = (vmt_feature['pop_employed'] / vmt_feature['hh']) * tRaw_trips_residential + tBtmp = (vmt_feature['vb_pop_employed_total'] / vmt_feature[ + 'vb_du_total']) * tRaw_trips_nonresidential ## 11may + tWorkers_per_HH = ( tAtmp + tBtmp ) / float(tRaw_trips_residential + tRaw_trips_nonresidential) ## 11may + + ## 11may + if vmt_feature['pop_age16_up'] - vmt_feature['pop_employed'] - vmt_feature['pop_age65_up'] < 0: + tAtmp = 0 + else: + tAtmp = ((vmt_feature['pop_age16_up'] - vmt_feature['pop_employed'] - vmt_feature[ + 'pop_age65_up']) / float( + vmt_feature['hh'])) * tRaw_trips_residential + + if vmt_feature['vb_pop_age16_up_total'] - vmt_feature['vb_pop_employed_total'] - vmt_feature[ + 'vb_pop_age65_up_total'] < 0: + tBtmp = 0 + else: + tBtmp = ((vmt_feature['vb_pop_age16_up_total'] - vmt_feature['vb_pop_employed_total'] - vmt_feature[ + 'vb_pop_age65_up_total']) / float(vmt_feature['vb_hh_total'])) * tRaw_trips_nonresidential + tNonWorkers_16_64_per_HH = ( tAtmp + tBtmp ) / float(tRaw_trips_residential + tRaw_trips_nonresidential) + + tAtmp = (vmt_feature['pop_age65_up'] / float(vmt_feature['hh'])) * tRaw_trips_residential + tBtmp = ((vmt_feature['vb_pop_age65_up_total']) / float( + vmt_feature['vb_hh_total'])) * tRaw_trips_nonresidential + tNonWorkers_65_per_HH = ( tAtmp + tBtmp ) / float(tRaw_trips_residential + tRaw_trips_nonresidential) + + tAtmp = ((vmt_feature['du_mf2to4'] + vmt_feature['du_mf5p']) / float( + vmt_feature['du']) ) * tRaw_trips_residential + tBtmp = ((vmt_feature['vb_du_mf_total']) / float( + vmt_feature['vb_du_total'])) * tRaw_trips_nonresidential + tMFU = ( tAtmp + tBtmp ) / float(tRaw_trips_residential + tRaw_trips_nonresidential) + + + ## -------------------------------------------------------------------------------------------- + ## -- [ indicator - income > 100k ] -- + if vmt_feature['hh_inc_00_10'] == None or vmt_feature['hh_inc_10_20'] == None or vmt_feature[ + 'hh_inc_20_30'] == None or vmt_feature[ + 'hh_inc_30_40'] == None or vmt_feature['hh_inc_40_50'] == None or vmt_feature['hh_inc_50_60'] == None or \ + vmt_feature['hh_inc_60_75'] == None or vmt_feature['hh_inc_75_100'] == None or vmt_feature[ + 'hh_inc_100p'] == None: + tSum = 0 + else: + tSum = \ + float(vmt_feature['hh_inc_00_10']) + float(vmt_feature['hh_inc_10_20']) + float( + vmt_feature['hh_inc_20_30']) + float( + vmt_feature['hh_inc_30_40']) + \ + float(vmt_feature['hh_inc_40_50']) + float(vmt_feature['hh_inc_50_60']) + float( + vmt_feature['hh_inc_60_75']) + float( + vmt_feature['hh_inc_75_100']) + \ + float(vmt_feature['hh_inc_100p']) + + if tSum == 0: + w89 = 0 + else: + w89 = vmt_feature['hh_inc_100p'] / float(tSum) + + if vmt_feature['vb_hh_inc_00_10_total'] == None or vmt_feature['vb_hh_inc_100p_total'] == None or vmt_feature[ + 'vb_hh_inc_10_20_total'] == None \ + or vmt_feature['vb_hh_inc_20_30_total'] == None or vmt_feature['vb_hh_inc_30_40_total'] == None or vmt_feature[ + 'vb_hh_inc_40_50_total'] == None or vmt_feature == None or ['vb_hh_inc_50_60_total'] == None or vmt_feature[ + 'vb_hh_inc_60_75_total'] == None or vmt_feature['vb_hh_inc_75_100_total'] == None: + tSum = 0 + else: + tSum = \ + vmt_feature['vb_hh_inc_00_10_total'] + vmt_feature['vb_hh_inc_10_20_total'] + vmt_feature[ + 'vb_hh_inc_20_30_total'] + vmt_feature[ + 'vb_hh_inc_30_40_total'] + \ + vmt_feature['vb_hh_inc_40_50_total'] + vmt_feature['vb_hh_inc_50_60_total'] + vmt_feature[ + 'vb_hh_inc_60_75_total'] + vmt_feature[ + 'vb_hh_inc_75_100_total'] + \ + vmt_feature['vb_hh_inc_100p_total'] + + if tSum == 0 or (tRaw_trips_residential + tRaw_trips_nonresidential) == 0: + tIncome100kPlus = 0 + else: + af89 = vmt_feature['vb_hh_inc_100p_total'] / float(tSum) + if af89 == 0 or tRaw_trips_nonresidential == 0: + tIncome100kPlus = 0 + else: + tAtmp = (w89 * tRaw_trips_residential) + tBtmp = (af89 * tRaw_trips_nonresidential) + tIncome100kPlus = ( tAtmp + tBtmp ) / float(tRaw_trips_residential + tRaw_trips_nonresidential) + + ## -------------------------------------------------------------------------------------------- + ## -- [ income category ] -- + + if vmt_feature['hh_inc_00_10'] == None or vmt_feature['hh_inc_10_20'] == None or vmt_feature[ + 'hh_inc_20_30'] == None or vmt_feature[ + 'hh_inc_30_40'] == None or vmt_feature['hh_inc_40_50'] == None or vmt_feature['hh_inc_50_60'] == None or \ + vmt_feature['hh_inc_60_75'] == None or vmt_feature['hh_inc_75_100'] == None or vmt_feature[ + 'hh_inc_100p'] == None: + tSum_O3W3 = 0 + else: + tSum_O3W3 = float(vmt_feature['hh_inc_00_10']) + float(vmt_feature['hh_inc_10_20']) + float( + vmt_feature['hh_inc_20_30']) + \ + float(vmt_feature['hh_inc_30_40']) + float(vmt_feature['hh_inc_40_50']) + float( + vmt_feature['hh_inc_50_60']) + \ + float(vmt_feature['hh_inc_60_75']) + float(vmt_feature['hh_inc_75_100']) + float( + vmt_feature['hh_inc_100p']) + #---------------- + if vmt_feature['vb_hh_inc_00_10_total'] == None or vmt_feature['vb_hh_inc_100p_total'] == None or vmt_feature[ + 'vb_hh_inc_10_20_total'] == None \ + or vmt_feature['vb_hh_inc_20_30_total'] == None or vmt_feature['vb_hh_inc_30_40_total'] == None or vmt_feature[ + 'vb_hh_inc_40_50_total'] == None or vmt_feature == None or ['vb_hh_inc_50_60_total'] == None or vmt_feature[ + 'vb_hh_inc_60_75_total'] == None or vmt_feature['vb_hh_inc_75_100_total'] == None: + tSum_AY3BG3 = 0 + else: + tSum_AY3BG3 = float( + vmt_feature['vb_hh_inc_00_10_total'] + vmt_feature['vb_hh_inc_10_20_total'] + vmt_feature[ + 'vb_hh_inc_20_30_total'] + \ + vmt_feature['vb_hh_inc_30_40_total'] + vmt_feature['vb_hh_inc_40_50_total'] + vmt_feature[ + 'vb_hh_inc_50_60_total'] + \ + vmt_feature['vb_hh_inc_60_75_total'] + vmt_feature['vb_hh_inc_75_100_total'] + vmt_feature[ + 'vb_hh_inc_100p_total']) + #---------------- + if tSum_O3W3 == 0: + o89 = 0 + p89 = 0 + q89 = 0 + r89 = 0 + s89 = 0 + t89 = 0 + u89 = 0 + v89 = 0 + w89 = 0 + else: + o89 = vmt_feature['hh_inc_00_10'] / tSum_O3W3 + p89 = vmt_feature['hh_inc_10_20'] / tSum_O3W3 + q89 = vmt_feature['hh_inc_20_30'] / tSum_O3W3 + r89 = vmt_feature['hh_inc_30_40'] / tSum_O3W3 + s89 = vmt_feature['hh_inc_40_50'] / tSum_O3W3 + t89 = vmt_feature['hh_inc_50_60'] / tSum_O3W3 + u89 = vmt_feature['hh_inc_60_75'] / tSum_O3W3 + v89 = vmt_feature['hh_inc_75_100'] / tSum_O3W3 + w89 = vmt_feature['hh_inc_100p'] / tSum_O3W3 + #---------------- + if tSum_AY3BG3 == 0: + x89 = 0 + y89 = 0 + z89 = 0 + aa89 = 0 + ab89 = 0 + ac89 = 0 + ad89 = 0 + ae89 = 0 + af89 = 0 + else: + x89 = vmt_feature['vb_hh_inc_00_10_total'] / tSum_AY3BG3 + y89 = vmt_feature['vb_hh_inc_10_20_total'] / tSum_AY3BG3 + z89 = vmt_feature['vb_hh_inc_20_30_total'] / tSum_AY3BG3 + aa89 = vmt_feature['vb_hh_inc_30_40_total'] / tSum_AY3BG3 + ab89 = vmt_feature['vb_hh_inc_40_50_total'] / tSum_AY3BG3 + ac89 = vmt_feature['vb_hh_inc_50_60_total'] / tSum_AY3BG3 + ad89 = vmt_feature['vb_hh_inc_60_75_total'] / tSum_AY3BG3 + ae89 = vmt_feature['vb_hh_inc_75_100_total'] / tSum_AY3BG3 + af89 = vmt_feature['vb_hh_inc_100p_total'] / tSum_AY3BG3 + #---------------- + tIC_upper = ( 1 * o89 + \ + 2 * ( p89 + q89 / 2.0 ) + \ + 3 * ( q89 / 2.0 + r89 / 2.0 ) + \ + 4 * ( r89 / 2.0 + s89 ) + \ + 5 * ( t89 + u89 ) + \ + 6 * v89 + \ + 7 * w89 / 2.0 + \ + 8 * w89 / 2.0 ) * tRaw_trips_residential + #---------------- + tIC_lower = ( 1 * x89 + \ + 2 * ( y89 + z89 / 2.0 ) + \ + 3 * ( z89 / 2.0 + aa89 / 2.0 ) + \ + 4 * ( aa89 / 2.0 + ab89 ) + \ + 5 * ( ac89 + ad89 ) + \ + 6 * ae89 + \ + 7 * w89 / 2.0 + \ + 8 * w89 / 2.0 ) * tRaw_trips_nonresidential + #---------------- + if (tRaw_trips_residential + tRaw_trips_nonresidential) == 0: + tIncomeCategory = 0 + else: + tIncomeCategory = ( tIC_upper + tIC_lower ) / float(tRaw_trips_residential + tRaw_trips_nonresidential) + + ## -------------------------------------------------------------------------------------------- + ## -- [ Log( net housing density ) ] -- + if vmt_feature['vb_du_total'] is None: + vmt_feature['vb_du_total'] = 0 + + if vmt_feature['vb_acres_parcel_res_total'] is None: + vmt_feature['vb_acres_parcel_res_total'] = 0 + + if vmt_feature['vb_acres_parcel_mixed_total'] is None: + vmt_feature['vb_acres_parcel_mixed_total'] = 0 + + if vmt_feature['acres_parcel_emp'] is None: + vmt_feature['acres_parcel_emp'] = 0 + + if vmt_feature['acres_parcel_mixed'] is None: + vmt_feature['acres_parcel_mixed'] = 0 + + tUpperSum = (vmt_feature['vb_du_total'] * float(tRaw_trips_nonresidential)) + ( + float(vmt_feature['du']) * float(tRaw_trips_residential)) + tLowerSum = ((vmt_feature['vb_acres_parcel_res_total'] + vmt_feature[ + 'vb_acres_parcel_mixed_total']) * float(tRaw_trips_nonresidential)) + ( + (vmt_feature['acres_parcel_res'] + vmt_feature['acres_parcel_mixed']) * float( + tRaw_trips_residential)) + + if tUpperSum <= 0 or tLowerSum <= 0: + tLogNetHousingDensity = 0 + else: + tLogNetHousingDensity = math.log(tUpperSum / float(tLowerSum)) + + ## -------------------------------------------------------------------------------------------- + ## -- [ Log( net retail density ) ] -- + if vmt_feature['vb_emp_retail_total'] is None: + vmt_feature['vb_emp_retail_total'] = 0 + + if vmt_feature['vb_acres_parcel_emp_total'] is None: + vmt_feature['vb_acres_parcel_emp_total'] = 0 + + + ##-- 30Aug11 raw_trips_residential/non is replaced by a new formula, only in the case of retail + E15 = vmt_feature['raw_trips_retail'] + tAllTrips = vmt_feature['raw_trips_sf'] + vmt_feature['raw_trips_mf24'] + vmt_feature['raw_trips_mf5'] + \ + vmt_feature['raw_trips_retail'] + vmt_feature['raw_trips_restaurant'] + vmt_feature[ + 'raw_trips_entertainment'] + \ + vmt_feature['raw_trips_office'] + vmt_feature['raw_trips_public'] + vmt_feature['raw_trips_industry'] + \ + vmt_feature['raw_trips_k12'] + vmt_feature['raw_trips_h_ed'] + + tXRawResSub = vmt_feature['raw_trips_sf'] + vmt_feature['raw_trips_mf24'] + vmt_feature['raw_trips_mf5'] + tXRawNonResSub = vmt_feature['raw_trips_retail'] + vmt_feature['raw_trips_office'] + \ + vmt_feature['raw_trips_public'] + vmt_feature['raw_trips_industry'] + \ + vmt_feature['raw_trips_k12'] + vmt_feature['raw_trips_h_ed'] + + tUpperSum = (vmt_feature['vb_emp_retail_total'] * (tAllTrips - E15)) + (vmt_feature['vb_emp_retail_total'] * E15) + tLowerSum = (vmt_feature['vb_acres_parcel_emp_total'] + (vmt_feature['vb_acres_parcel_mixed_total']) * ( + tAllTrips - E15)) + ( + ( vmt_feature['acres_parcel_emp'] + vmt_feature['acres_parcel_mixed']) * E15 ) + + if tUpperSum <= 0 or tLowerSum <= 0: + tLogNetRetailDensity = 0 + else: + tLogNetRetailDensity = math.log(tUpperSum / float(tLowerSum)) + + ## -------------------------------------------------------------------------------------------- + ## -- [ # of jobs within 45m of transit ] -- + + if vmt_feature['emp45m_transit'] is None: + tJobs45min = 0 + else: + tJobs45min = vmt_feature['emp45m_transit'] + + ## -------------------------------------------------------------------------------------------- + if (cAuto_const * cAuto_const_coef) + \ + (tWorkers_per_HH * cAuto_workers_per_hh_coef) + \ + (tNonWorkers_16_64_per_HH * cAuto_non_workers_16_64_hh_coef) + \ + (tNonWorkers_65_per_HH * cAuto_non_workers_65_hh_coef) + \ + (tIncomeCategory * cAuto_hh_income_coef) + \ + (tIncome100kPlus * cAuto_hh_income_gt_100k_coef) + \ + (tMFU * cAuto_hh_mfdu_coef) + \ + (tLogNetHousingDensity * cAuto_log_net_hu_dens_coef) + \ + (tLogNetRetailDensity * cAuto_log_net_retail_dens_coef) + \ + (tJobs45min * cAuto_jobs_45_trans_coef) < 0: + autos_per_hh = 0 + else: + autos_per_hh = (cAuto_const * cAuto_const_coef) + \ + (tWorkers_per_HH * cAuto_workers_per_hh_coef) + \ + (tNonWorkers_16_64_per_HH * cAuto_non_workers_16_64_hh_coef) + \ + (tNonWorkers_65_per_HH * cAuto_non_workers_65_hh_coef) + \ + (tIncomeCategory * cAuto_hh_income_coef) + \ + (tIncome100kPlus * cAuto_hh_income_gt_100k_coef) + \ + (tMFU * cAuto_hh_mfdu_coef) + \ + (tLogNetHousingDensity * cAuto_log_net_hu_dens_coef) + \ + (tLogNetRetailDensity * cAuto_log_net_retail_dens_coef) + \ + (tJobs45min * cAuto_jobs_45_trans_coef) + + return autos_per_hh + diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_calculate_final_results.py b/footprint/main/models/analysis_module/vmt_module/vmt_calculate_final_results.py new file mode 100644 index 000000000..31a574853 --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_calculate_final_results.py @@ -0,0 +1,423 @@ +import math + +__author__ = 'calthorpe' + + +def calculate_final_vmt_results(vmt_feature): + tF_trips_total = vmt_feature['trips_hbw'] + vmt_feature['trips_hbo'] + vmt_feature['trips_nhb'] + if tF_trips_total == 0: + vmt_feature['final_prod_hbw'] = 0.0 + vmt_feature['final_prod_hbo'] = 0.0 + vmt_feature['final_prod_nhb'] = 0.0 + + vmt_feature['final_attr_hbw'] = 0.0 + vmt_feature['final_attr_hbo'] = 0.0 + vmt_feature['final_attr_nhb'] = 0.0 + + vmt_feature['raw_trips_total'] = 0.0 + vmt_feature['vmt_daily'] = 0.0 + vmt_feature['vmt_daily_w_trucks'] = 0.0 + vmt_feature['vmt_daily_per_capita'] = 0.0 + vmt_feature['vmt_daily_per_hh'] = 0.0 + vmt_feature['vmt_annual'] = 0.0 + vmt_feature['vmt_annual_w_trucks'] = 0.0 + vmt_feature['vmt_annual_per_capita'] = 0.0 + vmt_feature['vmt_annual_per_hh'] = 0.0 + vmt_feature['vmt_daily_regression'] = 0.0 + vmt_feature['vmt_daily_mxd'] = 0.0 + vmt_feature['vmt_daily_raw'] = 0.0 + vmt_feature['regression_ratio'] = 0.0 + vmt_feature['pop_den_ratio'] = 0.0 + vmt_feature['hh_den_ratio'] = 0.0 + vmt_feature['emp_den_ratio'] = 0.0 + vmt_feature['emp30m_ratio'] = 0.0 + vmt_feature['auto_ownshp_ratio'] = 0 + vmt_feature['pop_den'] = 0.0 + vmt_feature['hh_den'] = 0.0 + vmt_feature['emp_den'] = 0.0 + vmt_feature['emp30m_den'] = 0.0 + vmt_feature['auto_ownshp_den'] = 0 + vmt_feature['external_veh_trips_reduction'] = 0.0 + vmt_feature['internal_capture_trips_total'] = 0.0 + vmt_feature['walking_trips_total'] = 0.0 + vmt_feature['transit_trips_total'] = 0.0 + vmt_feature['icpm_hbw'] = 0.0 + vmt_feature['icpm_hbo'] = 0.0 + vmt_feature['icpm_nhb'] = 0.0 + vmt_feature['wtpm_hbw'] = 0.0 + vmt_feature['wtpm_hbo'] = 0.0 + vmt_feature['wtpm_nhb'] = 0.0 + vmt_feature['ttpm_hbw'] = 0.0 + vmt_feature['ttpm_hbo'] = 0.0 + vmt_feature['ttpm_nhb'] = 0.0 + vmt_feature['trips_hbw_w_trucks'] = 0.0 + vmt_feature['trips_hbo_w_trucks'] = 0.0 + vmt_feature['trips_nhb_w_trucks'] = 0.0 + vmt_feature['trips_hbw_regression'] = 0.0 + vmt_feature['trips_hbo_regression'] = 0.0 + vmt_feature['trips_nhb_regression'] = 0.0 + + + return vmt_feature + + tF_num_icpm_hbw = vmt_feature['trips_hbw'] * ( + math.exp(vmt_feature['icpm_hbw']) / (math.exp(vmt_feature['icpm_hbw']) + 1.0)) + tF_num_icpm_hbo = vmt_feature['trips_hbo'] * ( + math.exp(vmt_feature['icpm_hbo']) / (math.exp(vmt_feature['icpm_hbo']) + 1.0)) + tF_num_icpm_nhb = vmt_feature['trips_nhb'] * ( + math.exp(vmt_feature['icpm_nhb']) / (math.exp(vmt_feature['icpm_nhb']) + 1.0)) + tF_num_icpm_total = tF_num_icpm_hbw + tF_num_icpm_hbo + tF_num_icpm_nhb + + tF_num_wtpm_hbw = (vmt_feature['trips_hbw'] - tF_num_icpm_hbw) * ( + math.exp(vmt_feature['wtpm_hbw']) / (math.exp(vmt_feature['wtpm_hbw']) + 1.0)) + tF_num_wtpm_hbo = (vmt_feature['trips_hbo'] - tF_num_icpm_hbo) * ( + math.exp(vmt_feature['wtpm_hbo']) / (math.exp(vmt_feature['wtpm_hbo']) + 1.0)) + tF_num_wtpm_nhb = (vmt_feature['trips_nhb'] - tF_num_icpm_nhb) * ( + math.exp(vmt_feature['wtpm_nhb']) / (math.exp(vmt_feature['wtpm_nhb']) + 1.0)) + tF_num_wtpm_total = tF_num_wtpm_hbw + tF_num_wtpm_hbo + tF_num_wtpm_nhb + + tF_num_ttpm_hbw = (vmt_feature['trips_hbw'] - tF_num_icpm_hbw) * ( + math.exp(vmt_feature['ttpm_hbw']) / (math.exp(vmt_feature['ttpm_hbw']) + 1.0)) + tF_num_ttpm_hbo = (vmt_feature['trips_hbo'] - tF_num_icpm_hbo) * ( + math.exp(vmt_feature['ttpm_hbo']) / (math.exp(vmt_feature['ttpm_hbo']) + 1.0)) + tF_num_ttpm_nhb = (vmt_feature['trips_nhb'] - tF_num_icpm_nhb) * ( + math.exp(vmt_feature['ttpm_nhb']) / (math.exp(vmt_feature['ttpm_nhb']) + 1.0)) + + tF_num_ttpm_total = tF_num_ttpm_hbw + tF_num_ttpm_hbo + tF_num_ttpm_nhb + + tF_net_ixxi_hbw = vmt_feature['trips_hbw'] - (tF_num_icpm_hbw + tF_num_wtpm_hbw + tF_num_ttpm_hbw) + tF_net_ixxi_hbo = vmt_feature['trips_hbo'] - (tF_num_icpm_hbo + tF_num_wtpm_hbo + tF_num_ttpm_hbo) + tF_net_ixxi_nhb = vmt_feature['trips_nhb'] - (tF_num_icpm_nhb + tF_num_wtpm_nhb + tF_num_ttpm_nhb) + tF_net_ixxi_total = tF_net_ixxi_hbw + tF_net_ixxi_hbo + tF_net_ixxi_nhb + + tF_external_veh_trips_reduction = 1.0 - (tF_net_ixxi_total / tF_trips_total) + + ##--------------------------------------------------- + + if vmt_feature['prod_hbw'] is not None: + tF_prod_trips_hbw = vmt_feature['prod_hbw'] + else: + tF_prod_trips_hbw = 0.0 + + if vmt_feature['prod_hbo'] is not None: + tF_prod_trips_hbo = vmt_feature['prod_hbo'] + else: + tF_prod_trips_hbo = 0.0 + + if vmt_feature['prod_nhb'] is not None: + tF_prod_trips_nhb = vmt_feature['prod_nhb'] + else: + tF_prod_trips_nhb = 0.0 + + ##------------ + if vmt_feature['attr_hbw'] is not None: + tF_attr_trips_hbw = vmt_feature['attr_hbw'] + else: + tF_attr_trips_hbw = 0 + + if vmt_feature['attr_hbo'] is not None: + tF_attr_trips_hbo = vmt_feature['attr_hbo'] + else: + tF_attr_trips_hbo = 0 + + if vmt_feature['attr_nhb'] is not None: + tF_attr_trips_nhb = vmt_feature['attr_nhb'] + else: + tF_attr_trips_nhb = 0 + + ##------------ + + tF_prod_hbw = tF_net_ixxi_hbw * vmt_feature['ite_prod_hbw'] / ( + vmt_feature['ite_prod_hbw'] + vmt_feature['ite_attr_hbw'] ) * tF_prod_trips_hbw / 2.0 + tF_prod_hbo = tF_net_ixxi_hbo * vmt_feature['ite_prod_hbo'] / ( + vmt_feature['ite_prod_hbo'] + vmt_feature['ite_attr_hbo'] ) * tF_prod_trips_hbo / 2.0 + tF_prod_nhb = tF_net_ixxi_nhb * vmt_feature['ite_prod_nhb'] / ( + vmt_feature['ite_prod_nhb'] + vmt_feature['ite_attr_nhb'] ) * tF_prod_trips_nhb / 2.0 + + tF_attr_hbw = tF_net_ixxi_hbw * vmt_feature['ite_attr_hbw'] / ( + vmt_feature['ite_prod_hbw'] + vmt_feature['ite_attr_hbw'] ) * (tF_attr_trips_hbw / 2.0) + tF_attr_hbo = tF_net_ixxi_hbo * vmt_feature['ite_attr_hbo'] / ( + vmt_feature['ite_prod_hbo'] + vmt_feature['ite_attr_hbo'] ) * tF_attr_trips_hbo / 2.0 + tF_attr_nhb = tF_net_ixxi_nhb * vmt_feature['ite_attr_nhb'] / ( + vmt_feature['ite_prod_nhb'] + vmt_feature['ite_attr_nhb'] ) * tF_attr_trips_nhb / 2.0 + + tF_hbw_total = tF_prod_hbw + tF_attr_hbw + tF_hbo_total = tF_prod_hbo + tF_attr_hbo + tF_nhb_total = tF_prod_nhb + tF_attr_nhb + + tF_grand_total = tF_hbw_total + tF_hbo_total + tF_nhb_total + tF_grand_total_trucks = tF_grand_total * (1 + float(vmt_feature['truck_adjustment_factor'])) + + + ##------------Raw VMT + + tF_prod_hbw_raw = vmt_feature['trips_hbw'] * vmt_feature['ite_prod_hbw'] / ( + vmt_feature['ite_prod_hbw'] + vmt_feature['ite_attr_hbw'] ) * tF_prod_trips_hbw / 2.0 + tF_prod_hbo_raw = vmt_feature['trips_hbo'] * vmt_feature['ite_prod_hbo'] / ( + vmt_feature['ite_prod_hbo'] + vmt_feature['ite_attr_hbo'] ) * tF_prod_trips_hbo / 2.0 + tF_prod_nhb_raw = vmt_feature['trips_nhb'] * vmt_feature['ite_prod_nhb'] / ( + vmt_feature['ite_prod_nhb'] + vmt_feature['ite_attr_nhb'] ) * tF_prod_trips_nhb / 2.0 + + tF_attr_hbw_raw = vmt_feature['trips_hbw'] * vmt_feature['ite_attr_hbw'] / ( + vmt_feature['ite_prod_hbw'] + vmt_feature['ite_attr_hbw'] ) * (tF_attr_trips_hbw / 2.0) + tF_attr_hbo_raw = vmt_feature['trips_hbo'] * vmt_feature['ite_attr_hbo'] / ( + vmt_feature['ite_prod_hbo'] + vmt_feature['ite_attr_hbo'] ) * tF_attr_trips_hbo / 2.0 + tF_attr_nhb_raw = vmt_feature['trips_nhb'] * vmt_feature['ite_attr_nhb'] / ( + vmt_feature['ite_prod_nhb'] + vmt_feature['ite_attr_nhb'] ) * tF_attr_trips_nhb / 2.0 + + tF_hbw_total_raw = tF_prod_hbw_raw + tF_attr_hbw_raw + tF_hbo_total_raw = tF_prod_hbo_raw + tF_attr_hbo_raw + tF_nhb_total_raw = tF_prod_nhb_raw + tF_attr_nhb_raw + + tF_grand_total_raw = tF_hbw_total_raw + tF_hbo_total_raw + tF_nhb_total_raw + + + #regression calculations for adjustment to vmt + + auto_ownshp = vmt_feature['autos_per_hh'] + if vmt_feature['hh'] > 0.0: + hh_den = vmt_feature['pop'] / vmt_feature['hh'] + elif vmt_feature['pop'] is None or vmt_feature['hh'] is None: + hh_den = 0.0 + else: + hh_den = 0.0 + + if vmt_feature['acres_parcel_res'] + vmt_feature['acres_parcel_emp'] + vmt_feature['acres_parcel_mixed'] > 0.0: + pop_den = vmt_feature['pop'] / ( + vmt_feature['acres_parcel_res'] + vmt_feature['acres_parcel_emp'] + vmt_feature['acres_parcel_mixed']) + elif vmt_feature['acres_parcel_res'] + vmt_feature['acres_parcel_emp'] + vmt_feature[ + 'acres_parcel_mixed'] is None or vmt_feature[ + 'pop'] is None: + pop_den = 0.0 + else: + pop_den = 0.0 + + if vmt_feature['acres_parcel_res'] + vmt_feature['acres_parcel_emp'] + vmt_feature['acres_parcel_mixed'] > 0.0: + emp_den = (vmt_feature['emp_retail'] + vmt_feature['emp_restaccom'] + vmt_feature['emp_arts_entertainment'] + + vmt_feature[ + 'emp_office'] + vmt_feature['emp_public'] + vmt_feature['emp_industry']) / ( + vmt_feature['acres_parcel_res'] + vmt_feature['acres_parcel_emp'] + vmt_feature[ + 'acres_parcel_mixed']) + elif (vmt_feature['emp_retail'] + vmt_feature['emp_restaccom'] + vmt_feature['emp_arts_entertainment'] + + vmt_feature[ + 'emp_office'] + vmt_feature[ + 'emp_public'] + vmt_feature['emp_industry']) is None or vmt_feature['acres_parcel_res'] + vmt_feature[ + 'acres_parcel_emp'] + \ + vmt_feature['acres_parcel_mixed'] is None: + emp_den = 0.0 + else: + emp_den = 0.0 + + if vmt_feature['emp30m_transit'] is None: + emp30m_den = 0.0 + else: + emp30m_den = vmt_feature['emp30m_transit'] / vmt_feature['total_employment'] + + auto_ownshp_ratio = (auto_ownshp / 1.89) * 0.446 + hh_den_ratio = (hh_den / 2.33) * 0.371 + pop_den_ratio = (pop_den / 10.957) * -0.023 + emp_den_ratio = (emp_den / 5.055) * -0.015 + emp30m_ratio = emp30m_den * -0.439 + + regression_ratio = 0.225 + auto_ownshp_ratio + hh_den_ratio + pop_den_ratio + emp_den_ratio + emp30m_ratio + + if 1 - regression_ratio > tF_external_veh_trips_reduction: + auto_ownshp_ratio = vmt_feature['hh_avg_veh'] + regression_ratio = 0.225 + auto_ownshp_ratio + hh_den_ratio + pop_den_ratio + emp_den_ratio + emp30m_ratio + + if regression_ratio < 1 - tF_external_veh_trips_reduction: + regression_ratio = 1 - tF_external_veh_trips_reduction + vmt_feature['regression_ratio'] = regression_ratio + else: + vmt_feature['regression_ratio'] = regression_ratio + else: + vmt_feature['regression_ratio'] = regression_ratio + + #if regression_ratio < 1.3: + #regression_ratio = 1.3 + #vmt_feature['regression_ratio'] = regression_ratio + #else: + #vmt_feature['regression_ratio'] = regression_ratio + + vmt_feature['pop_den_ratio'] = pop_den_ratio + vmt_feature['hh_den_ratio'] = hh_den_ratio + vmt_feature['emp_den_ratio'] = emp_den_ratio + vmt_feature['emp30m_ratio'] = emp30m_ratio + vmt_feature['auto_ownshp_ratio'] = auto_ownshp_ratio + + vmt_feature['pop_den'] = pop_den + vmt_feature['hh_den'] = hh_den + vmt_feature['emp_den'] = emp_den + vmt_feature['emp30m_den'] = emp30m_den + vmt_feature['auto_ownshp_den'] = (auto_ownshp / 1.89) + #regression adjusted vmt + + tF_prod_hbw_rg = (vmt_feature['trips_hbw'] * regression_ratio) * vmt_feature['ite_prod_hbw'] / ( + vmt_feature['ite_prod_hbw'] + vmt_feature['ite_attr_hbw'] ) * tF_prod_trips_hbw / 2.0 + tF_prod_hbo_rg = (vmt_feature['trips_hbo'] * regression_ratio) * vmt_feature['ite_prod_hbo'] / ( + vmt_feature['ite_prod_hbo'] + vmt_feature['ite_attr_hbo'] ) * tF_prod_trips_hbo / 2.0 + tF_prod_nhb_rg = (vmt_feature['trips_nhb'] * regression_ratio) * vmt_feature['ite_prod_nhb'] / ( + vmt_feature['ite_prod_nhb'] + vmt_feature['ite_attr_nhb'] ) * tF_prod_trips_nhb / 2.0 + + tF_attr_hbw_rg = (vmt_feature['trips_hbw'] * regression_ratio) * vmt_feature['ite_attr_hbw'] / ( + vmt_feature['ite_prod_hbw'] + vmt_feature['ite_attr_hbw'] ) * tF_attr_trips_hbw / 2.0 + tF_attr_hbo_rg = (vmt_feature['trips_hbo'] * regression_ratio) * vmt_feature['ite_attr_hbo'] / ( + vmt_feature['ite_prod_hbo'] + vmt_feature['ite_attr_hbo'] ) * tF_attr_trips_hbo / 2.0 + tF_attr_nhb_rg = (vmt_feature['trips_nhb'] * regression_ratio) * vmt_feature['ite_attr_nhb'] / ( + vmt_feature['ite_prod_nhb'] + vmt_feature['ite_attr_nhb'] ) * tF_attr_trips_nhb / 2.0 + + tF_hbw_total_rg = tF_prod_hbw_rg + tF_attr_hbw_rg + tF_hbo_total_rg = tF_prod_hbo_rg + tF_attr_hbo_rg + tF_nhb_total_rg = tF_prod_nhb_rg + tF_attr_nhb_rg + + tF_grand_total_rg = tF_hbw_total_rg + tF_hbo_total_rg + tF_nhb_total_rg + tF_grand_total_rg_trucks = tF_grand_total_rg * (1 + float(vmt_feature['truck_adjustment_factor'])) + + if tF_external_veh_trips_reduction < 0.1: + final_vmt_total = tF_grand_total_rg + else: + final_vmt_total = tF_grand_total + + if tF_external_veh_trips_reduction < 0.1: + vmt_feature['trips_hbw_w_trucks'] = vmt_feature['trips_hbw'] * regression_ratio * ( + 1 + float(vmt_feature['truck_adjustment_factor'])) + else: + vmt_feature['trips_hbw_w_trucks'] = vmt_feature['trips_hbw'] * (1 + float(vmt_feature['truck_adjustment_factor'])) + if tF_external_veh_trips_reduction < 0.1: + vmt_feature['trips_hbo_w_trucks'] = vmt_feature['trips_hbo'] * regression_ratio * ( + 1 + float(vmt_feature['truck_adjustment_factor'])) + else: + vmt_feature['trips_hbo_w_trucks'] = vmt_feature['trips_hbo'] * (1 + float(vmt_feature['truck_adjustment_factor'])) + + if tF_external_veh_trips_reduction < 0.1: + vmt_feature['trips_nhb_w_trucks'] = vmt_feature['trips_nhb'] * regression_ratio * ( + 1 + float(vmt_feature['truck_adjustment_factor'])) + else: + vmt_feature['trips_nhb_w_trucks'] = vmt_feature['trips_nhb'] * (1 + float(vmt_feature['truck_adjustment_factor'])) + + if tF_external_veh_trips_reduction < 0.1: + vmt_feature['trips_hbw_regression'] = vmt_feature['trips_hbw'] * regression_ratio + else: + vmt_feature['trips_hbw_regression'] = vmt_feature['trips_hbw'] + + if tF_external_veh_trips_reduction < 0.1: + vmt_feature['trips_hbo_regression'] = vmt_feature['trips_hbo'] * regression_ratio + else: + vmt_feature['trips_hbo_regression'] = vmt_feature['trips_hbo'] + + if tF_external_veh_trips_reduction < 0.1: + vmt_feature['trips_nhb_regression'] = vmt_feature['trips_nhb'] * regression_ratio + else: + vmt_feature['trips_nhb_regression'] = vmt_feature['trips_nhb'] + + if tF_external_veh_trips_reduction < 0.1: + final_vmt_total_trucks = tF_grand_total_rg_trucks + else: + final_vmt_total_trucks = tF_grand_total_trucks + + #productions by type + if tF_external_veh_trips_reduction < 0.1: + final_prod_hbw = tF_prod_hbw_rg + else: + final_prod_hbw = tF_prod_hbw + + if tF_external_veh_trips_reduction < 0.1: + final_prod_hbo = tF_prod_hbo_rg + else: + final_prod_hbo = tF_prod_hbo + + if tF_external_veh_trips_reduction < 0.1: + final_prod_nhb = tF_prod_nhb_rg + else: + final_prod_nhb = tF_prod_nhb + + #attractions by type + if tF_external_veh_trips_reduction < 0.1: + vmt_feature['final_attr_hbw'] = tF_attr_hbw_rg + else: + vmt_feature['final_attr_hbw'] = tF_attr_hbw + + if tF_external_veh_trips_reduction < 0.1: + vmt_feature['final_attr_hbo'] = tF_attr_hbo_rg + else: + vmt_feature['final_attr_hbo'] = tF_attr_hbo + + if tF_external_veh_trips_reduction < 0.1: + vmt_feature['final_attr_nhb'] = tF_attr_nhb_rg + else: + vmt_feature['final_attr_nhb'] = tF_attr_nhb + + if (vmt_feature['qmb_acres_parcel_res_total'] + vmt_feature['qmb_acres_parcel_emp_total'] + vmt_feature[ + 'qmb_acres_parcel_mixed_total']) != 0: + vmt_feature['pop_emp_sqmi'] = ( vmt_feature['qmb_pop_total'] + vmt_feature['qmb_pop_total']) / ( + vmt_feature['qmb_acres_parcel_res_total'] + vmt_feature['qmb_acres_parcel_emp_total'] + vmt_feature[ + 'qmb_acres_parcel_mixed_total'] ) * 640.0 + else: + vmt_feature['pop_emp_sqmi'] = 0 + + vmt_feature['intersections_sqmi'] = vmt_feature['intersections_qtrmi'] + + vmt_feature['prop_regional_emp30m_transit'] = emp30m_den + + if vmt_feature['hh_within_quarter_mile_trans'] > 0: ##23mar + vmt_feature['prop_hh_within_quarter_mile_trans'] = 1.0 + else: + vmt_feature['prop_hh_within_quarter_mile_trans'] = 0.0 + + vmt_feature['icpm_hbw'] = tF_num_icpm_hbw + vmt_feature['icpm_hbo'] = tF_num_icpm_hbo + vmt_feature['icpm_nhb'] = tF_num_icpm_nhb + vmt_feature['wtpm_hbw'] = tF_num_wtpm_hbw + vmt_feature['wtpm_hbo'] = tF_num_wtpm_hbo + vmt_feature['wtpm_nhb'] = tF_num_wtpm_nhb + vmt_feature['ttpm_hbw'] = tF_num_ttpm_hbw + vmt_feature['ttpm_hbo'] = tF_num_ttpm_hbo + vmt_feature['ttpm_nhb'] = tF_num_ttpm_nhb + vmt_feature['external_veh_trips_reduction'] = tF_external_veh_trips_reduction + + vmt_feature['vmt_daily_regression'] = tF_grand_total_rg + vmt_feature['vmt_daily_mxd'] = tF_grand_total + vmt_feature['vmt_daily_raw'] = tF_grand_total_raw + + ##---------------------------------------- + vmt_feature['final_prod_hbw'] = final_prod_hbw + vmt_feature['final_prod_hbo'] = final_prod_hbo + vmt_feature['final_prod_nhb'] = final_prod_nhb + + ##----------------------------------------- + vmt_feature['internal_capture_trips_total'] = tF_num_icpm_total + vmt_feature['walking_trips_total'] = tF_num_wtpm_total + vmt_feature['transit_trips_total'] = tF_num_ttpm_total + + vmt_feature['raw_trips_total'] = tF_trips_total + vmt_feature['vmt_daily'] = final_vmt_total + vmt_feature['vmt_daily_w_trucks'] = final_vmt_total_trucks + + if float(vmt_feature['pop']) > 0: + vmt_feature['vmt_daily_per_capita'] = ( + float(((final_prod_hbw + final_prod_hbo + final_prod_nhb) * 2)) / float(vmt_feature['pop'])) + else: + vmt_feature['vmt_daily_per_capita'] = 0 + + if float(vmt_feature['hh']) > 0: + vmt_feature['vmt_daily_per_hh'] = ( + float(((final_prod_hbw + final_prod_hbo + final_prod_nhb) * 2)) / float(vmt_feature['hh'])) + else: + vmt_feature['vmt_daily_per_hh'] = 0 + + vmt_feature['vmt_annual'] = final_vmt_total * 350.0 + vmt_feature['vmt_annual_w_trucks'] = final_vmt_total_trucks * 350.0 + + if float(vmt_feature['pop']) > 0: + vmt_feature['vmt_annual_per_capita'] = ( + float((((final_prod_hbw + final_prod_hbo + final_prod_nhb) * 2) * 347.0)) / float(vmt_feature['pop'])) + else: + vmt_feature['vmt_annual_per_capita'] = 0 + + if float(vmt_feature['hh']) > 0: + vmt_feature['vmt_annual_per_hh'] = ( + float((((final_prod_hbw + final_prod_hbo + final_prod_nhb) * 2) * 347.0)) / float(vmt_feature['hh'])) + else: + vmt_feature['vmt_annual_per_hh'] = 0 + + return vmt_feature diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_calculate_log_odds.py b/footprint/main/models/analysis_module/vmt_module/vmt_calculate_log_odds.py new file mode 100644 index 000000000..a41838ed4 --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_calculate_log_odds.py @@ -0,0 +1,235 @@ +import math + +from footprint.main.models.analysis_module.vmt_module.vmt_model_constants import cICPM_hbw_110, cICPM_hbw_113, cICPM_hbw_114, cICPM_hbw_107, cICPM_hbo_109, cICPM_hbo_110, cICPM_hbo_112, cICPM_hbo_113, cICPM_hbo_114, cICPM_hbo_107, cICPM_nhb_107, cICPM_nhb_108, cICPM_nhb_109, cICPM_nhb_112, cICPM_nhb_113, cICPM_nhb_114, cWTPM_hbw_121, cWTPM_hbw_124, cWTPM_hbw_127, cWTPM_hbw_128, cWTPM_hbw_129, cWTPM_hbo_121, cWTPM_hbo_122, cWTPM_hbo_123, cWTPM_hbo_124, cWTPM_hbo_126, cWTPM_hbo_127, cWTPM_hbo_128, cWTPM_hbo_129, cWTPM_nhb_121, cWTPM_nhb_123, cWTPM_nhb_126, cWTPM_nhb_127, cWTPM_nhb_128, cWTPM_nhb_129, cTTPM_hbw_138, cTTPM_hbw_136, cTTPM_hbw_139, cTTPM_hbw_140, cTTPM_hbw_141, cTTPM_hbw_142, cTTPM_hbo_136, cTTPM_hbo_137, cTTPM_hbo_140, cTTPM_hbo_141, cTTPM_hbo_142, cTTPM_nhb_136, cTTPM_nhb_139, cTTPM_nhb_142 +from footprint.main.models.analysis_module.vmt_module.vmt_auto_ownership_model import generate_predicted_auto_ownership + +__author__ = 'calthorpe' + + +def calculate_log_odds(vmt_feature): + autos_per_hh = generate_predicted_auto_ownership(vmt_feature) + if autos_per_hh < vmt_feature['hh_avg_veh'] * 0.5 or autos_per_hh > 5.0: + autos_per_hh = vmt_feature['hh_avg_veh'] + + tRes = 0 + + ##-- use qmb + tMXD_total_pop = vmt_feature['qmb_pop_total'] + + ##-- use single cell and qmb + tMXD_total_emp_qmb = vmt_feature['qmb_emp_total'] + if vmt_feature['emp_retail'] is None: vmt_feature['emp_retail'] = 0 + + ##-- 30Aug11 no more service category, restaurant is added + if vmt_feature['emp_restaccom'] is None: vmt_feature['emp_restaccom'] = 0 + if vmt_feature['emp_arts_entertainment'] is None: vmt_feature['emp_arts_entertainment'] = 0 + + if vmt_feature['emp_office'] is None: vmt_feature['emp_office'] = 0 + if vmt_feature['emp_public'] is None: vmt_feature['emp_public'] = 0 + if vmt_feature['emp_industry'] is None: vmt_feature['emp_industry'] = 0 + + tMXD_total_emp_cell = vmt_feature['emp_retail'] + vmt_feature['emp_arts_entertainment'] + vmt_feature[ + 'emp_restaccom'] + \ + vmt_feature['emp_office'] \ + + vmt_feature['emp_public'] + vmt_feature['emp_industry'] + + if (float(vmt_feature['acres_parcel_res']) + float(vmt_feature['acres_parcel_emp']) + float( + vmt_feature['acres_parcel_mixed'])) <= 0: + tMXD_area = 0 + else: + tMXD_area = (float(vmt_feature['acres_parcel_res']) + float(vmt_feature['acres_parcel_emp']) + float( + vmt_feature['acres_parcel_mixed'])) / 640.0 + + if (0.2 * (tMXD_total_pop + tMXD_total_emp_qmb) ) <= 0: + tCT = 0 + else: + tCT = ( 1.0 - abs(0.2 * tMXD_total_pop - tMXD_total_emp_qmb) / (0.2 * tMXD_total_pop + tMXD_total_emp_qmb)) + tMXD_jobs_v_pop = max(tCT, 0.01) + + tMXD_retail_jobs_v_pop = 0 + + if vmt_feature['intersections_qtrmi'] > 0: + tMXD_intersections = float(vmt_feature['intersections_qtrmi']) + else: + tMXD_intersections = 0.0 + + ## Q. if hh_avg_size is not available, should that be a fatal error? + ## well, about two--thirds of records have hh_avg_size == 0 + tMXD_hh_avg_size = vmt_feature['hh_avg_size'] + if tMXD_hh_avg_size == 0: + tMXD_vehicles_per_capita = 0.8 #12nov10 + tMXD_hh_avg_size = 2.577 + else: + tMXD_vehicles_per_capita = autos_per_hh / tMXD_hh_avg_size + + if tMXD_vehicles_per_capita < 0: tMXD_vehicles_per_capita = 0 + + if tMXD_jobs_v_pop > 0: + tICPM_hbw_jobs_pop = math.log(tMXD_jobs_v_pop) * cICPM_hbw_110 + else: + tICPM_hbw_jobs_pop = 0.0 + + if tMXD_hh_avg_size > 0: + tICPM_hbw_hhsize = math.log(tMXD_hh_avg_size) * cICPM_hbw_113 + else: + tICPM_hbw_hhsize = 0.0 + + if tMXD_vehicles_per_capita > 0: + tICPM_hbw_veh = math.log(tMXD_vehicles_per_capita) * cICPM_hbw_114 + else: + tICPM_hbw_veh = 0.0 + + tICPM_hbw = cICPM_hbw_107 + tICPM_hbw_jobs_pop + tICPM_hbw_hhsize + tICPM_hbw_veh + + if tMXD_area > 0: + tICPM_hbo_area = math.log(tMXD_area) * cICPM_hbo_109 + else: + tICPM_hbo_area = 0.0 + + if tMXD_jobs_v_pop > 0: + tICPM_hbo_jobs = math.log(tMXD_jobs_v_pop) * cICPM_hbo_110 + else: + tICPM_hbo_jobs = 0.0 + + if tMXD_intersections > 0: + tICPM_hbo_int = math.log(tMXD_intersections) * cICPM_hbo_112 + else: + tICPM_hbo_int = 0.0 + + if tMXD_hh_avg_size > 0: + tICPM_hbo_hhavg = math.log(tMXD_hh_avg_size) * cICPM_hbo_113 + else: + tICPM_hbo_hhavg = 0.0 + + if tMXD_vehicles_per_capita > 0: + tICPM_hbo_veh = math.log(tMXD_vehicles_per_capita) * cICPM_hbo_114 + else: + tICPM_hbo_veh = 0.0 + + tICPM_hbo = cICPM_hbo_107 + tICPM_hbo_area + tICPM_hbo_jobs + tICPM_hbo_int + tICPM_hbo_hhavg + tICPM_hbo_veh + + + #---------------------------------------------------------- + tICPM_nhb = cICPM_nhb_107 + if tMXD_total_emp_cell > 0: + tICPM_nhb = tICPM_nhb + ( math.log(tMXD_total_emp_cell) * cICPM_nhb_108 ) + if tMXD_area > 0: + tICPM_nhb = tICPM_nhb + ( math.log(tMXD_area) * cICPM_nhb_109 ) + if tMXD_intersections > 0: + tICPM_nhb = tICPM_nhb + ( math.log(tMXD_intersections) * cICPM_nhb_112 ) + if tMXD_hh_avg_size > 0: + tICPM_nhb = tICPM_nhb + ( math.log(tMXD_hh_avg_size) * cICPM_nhb_113 ) + if tMXD_vehicles_per_capita > 0: + tICPM_nhb = tICPM_nhb + ( math.log(tMXD_vehicles_per_capita) * cICPM_nhb_114 ) + + + #-- Walking Trip Probability Model -------------------- + if (vmt_feature['qmb_acres_parcel_res_total'] + vmt_feature['qmb_acres_parcel_emp_total'] + vmt_feature[ + 'qmb_acres_parcel_mixed_total']) != 0: + tMXD_pop_emp_m_sq = ( tMXD_total_pop + tMXD_total_emp_qmb) / ( + vmt_feature['qmb_acres_parcel_res_total'] + vmt_feature['qmb_acres_parcel_emp_total'] + vmt_feature[ + 'qmb_acres_parcel_mixed_total'] ) * 640.0 + else: + tMXD_pop_emp_m_sq = 0 + if vmt_feature['emp_within_1mile'] == 0 or vmt_feature['emp_within_1mile'] is None: + tMXD_emp_1m = 0 + else: + tMXD_emp_1m = float(vmt_feature['emp_within_1mile']) + + #---------------------------------------------------------- + tWTPM_hbw = cWTPM_hbw_121 + if tMXD_jobs_v_pop > 0: + tWTPM_hbw = tWTPM_hbw + ( math.log(tMXD_jobs_v_pop) * cWTPM_hbw_124 ) + if tMXD_emp_1m > 0: + tWTPM_hbw = tWTPM_hbw + ( math.log(tMXD_emp_1m) * cWTPM_hbw_127 ) + if tMXD_hh_avg_size > 0: + tWTPM_hbw = tWTPM_hbw + ( math.log(tMXD_hh_avg_size) * cWTPM_hbw_128 ) + if tMXD_vehicles_per_capita > 0: + tWTPM_hbw = tWTPM_hbw + ( math.log(tMXD_vehicles_per_capita) * cWTPM_hbw_129 ) + + #---------------------------------------------------------- + tWTPM_hbo = cWTPM_hbo_121 + if tMXD_area > 0: + tWTPM_hbo = tWTPM_hbo + ( math.log(tMXD_area) * cWTPM_hbo_122 ) + if tMXD_pop_emp_m_sq > 0: + tWTPM_hbo = tWTPM_hbo + ( math.log(tMXD_pop_emp_m_sq) * cWTPM_hbo_123 ) + if tMXD_jobs_v_pop > 0: + tWTPM_hbo = tWTPM_hbo + ( math.log(tMXD_jobs_v_pop) * cWTPM_hbo_124 ) + if tMXD_intersections > 0: + tWTPM_hbo = tWTPM_hbo + ( math.log(tMXD_intersections) * cWTPM_hbo_126 ) + if tMXD_emp_1m > 0: + tWTPM_hbo = tWTPM_hbo + ( math.log(tMXD_emp_1m) * cWTPM_hbo_127 ) + if tMXD_hh_avg_size > 0: + tWTPM_hbo = tWTPM_hbo + ( math.log(tMXD_hh_avg_size) * cWTPM_hbo_128 ) + if tMXD_vehicles_per_capita > 0: + tWTPM_hbo = tWTPM_hbo + ( math.log(tMXD_vehicles_per_capita) * cWTPM_hbo_129 ) + + #---------------------------------------------------------- + tWTPM_nhb = cWTPM_nhb_121 + if tMXD_pop_emp_m_sq > 0: + tWTPM_nhb = tWTPM_nhb + ( math.log(tMXD_pop_emp_m_sq) * cWTPM_nhb_123 ) + if tMXD_intersections > 0: + tWTPM_nhb = tWTPM_nhb + ( math.log(tMXD_intersections) * cWTPM_nhb_126 ) + if tMXD_emp_1m > 0: + tWTPM_nhb = tWTPM_nhb + ( math.log(tMXD_emp_1m) * cWTPM_nhb_127 ) + if tMXD_hh_avg_size > 0: + tWTPM_nhb = tWTPM_nhb + ( math.log(tMXD_hh_avg_size) * cWTPM_nhb_128 ) + if tMXD_vehicles_per_capita > 0: + tWTPM_nhb = tWTPM_nhb + ( math.log(tMXD_vehicles_per_capita) * cWTPM_nhb_129 ) + + #-- Transit Trip Probability Model -------------------- + if vmt_feature['emp30m_transit'] is not None and vmt_feature['total_employment'] > 0: + tMXD_emp_30_min_transit = vmt_feature['emp30m_transit'] / vmt_feature['total_employment'] ##13May + else: + tMXD_emp_30_min_transit = 0 + + if vmt_feature['hh_within_quarter_mile_trans'] > 0: ##23mar + tMXD_hh_quarter_m_transit = 1.0 + else: + tMXD_hh_quarter_m_transit = 0.0 + + #---------------------------------------------------------- + tTTPM_hbw = cTTPM_hbw_136 + if tMXD_intersections > 0: + tTTPM_hbw = tTTPM_hbw + ( math.log(tMXD_intersections) * cTTPM_hbw_138 ) + #-------------------------------- + ## 26apr11 - divide by gTotalAreaEmployment as per mk + if tMXD_emp_30_min_transit > 0: + tTTPM_hbw = tTTPM_hbw + ( math.log(tMXD_emp_30_min_transit) * cTTPM_hbw_139 ) + if tMXD_hh_quarter_m_transit > 0: + tTTPM_hbw = tTTPM_hbw + ( math.log(tMXD_hh_quarter_m_transit) * cTTPM_hbw_140 ) + if tMXD_hh_avg_size > 0: + tTTPM_hbw = tTTPM_hbw + ( math.log(tMXD_hh_avg_size) * cTTPM_hbw_141 ) + if tMXD_vehicles_per_capita > 0: + tTTPM_hbw = tTTPM_hbw + ( math.log(tMXD_vehicles_per_capita) * cTTPM_hbw_142 ) + + #---------------------------------------------------------- + tTTPM_hbo = cTTPM_hbo_136 + if tMXD_pop_emp_m_sq > 0: + tTTPM_hbo = tTTPM_hbo + ( math.log(tMXD_pop_emp_m_sq) * cTTPM_hbo_137 ) + if tMXD_hh_quarter_m_transit > 0: + tTTPM_hbo = tTTPM_hbo + ( math.log(tMXD_hh_quarter_m_transit) * cTTPM_hbo_140 ) + if tMXD_hh_avg_size > 0: + tTTPM_hbo = tTTPM_hbo + ( math.log(tMXD_hh_avg_size) * cTTPM_hbo_141 ) + if tMXD_vehicles_per_capita > 0: + tTTPM_hbo = tTTPM_hbo + ( math.log(tMXD_vehicles_per_capita) * cTTPM_hbo_142 ) + + #---------------------------------------------------------- + tTTPM_nhb = cTTPM_nhb_136 + if tMXD_emp_30_min_transit > 0: + tTTPM_nhb = tTTPM_nhb + ( math.log(tMXD_emp_30_min_transit) * cTTPM_nhb_139 ) + if tMXD_vehicles_per_capita > 0: + tTTPM_nhb = tTTPM_nhb + ( math.log(tMXD_vehicles_per_capita) * cTTPM_nhb_142 ) + + vmt_feature['autos_per_hh'] = autos_per_hh + vmt_feature['icpm_hbw'] = tICPM_hbw + vmt_feature['icpm_hbo'] = tICPM_hbo + vmt_feature['icpm_nhb'] = tICPM_nhb + vmt_feature['wtpm_hbw'] = tWTPM_hbw + vmt_feature['wtpm_hbo'] = tWTPM_hbo + vmt_feature['wtpm_nhb'] = tWTPM_nhb + vmt_feature['ttpm_hbw'] = tTTPM_hbw + vmt_feature['ttpm_hbo'] = tTTPM_hbo + vmt_feature['ttpm_nhb'] = tTTPM_nhb + + return vmt_feature \ No newline at end of file diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_model_constants.py b/footprint/main/models/analysis_module/vmt_module/vmt_model_constants.py new file mode 100644 index 000000000..b933d3fb7 --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_model_constants.py @@ -0,0 +1,164 @@ +__author__ = 'calthorpe' + + +#****************************************************************************** +# Constants +#****************************************************************************** + +# 1 -- Trip Generations ------------------------ +cLogMultiplier_du_det_df = 0.92 +cLogMultiplier_emp_retail = 0.65 +cLogMultiplier_emp_vmt_office = 0.84 +cLogMultiplier_emp_vmt_public = 0.84 + +cLogConstant_du_det_df = 2.71 +cLogConstant_emp_retail = 5.83 +cLogConstant_emp_vmt_office = 2.23 +cLogConstant_emp_vmt_public = 2.23 + +cLinearMult_mf2to4 = 6.06 +cLinearConst_mf2to4 = 123.56 +cLinearMult_mf5p = 3.77 +cLinearConst_mf5p = 223.66 ##-- 21mar11 +cLinearMult_emp_industry = 2.95 +cLinearConst_emp_industry = 30.57 + +cJobConversionRate = 2 + +# 2 -- Trip Purpose ------------------------ + +# NCHRP Factors +cProd_perc_per_hh = 8.85 +cProd_perc_hbw = 0.21 +cProd_perc_hbo = 0.5625 +cProd_perc_nhb = 0.2275 + +cAttractions_per_hh_hbw = 0 +cAttractions_per_hh_hbo = 0.90 +cAttractions_per_hh_nhb = 0.50 + +cAttractions_per_ret_emp_hbw = 1.45 +cAttractions_per_ret_emp_hbo = 9.0 +cAttractions_per_ret_emp_nhb = 4.10 + +cAttractions_per_service_emp_hbw = 1.45 +cAttractions_per_service_emp_hbo = 1.70 +cAttractions_per_service_emp_nhb = 1.20 + +cAttractions_per_ind_emp_hbw = 1.45 +cAttractions_per_ind_emp_hbo = 0.5 +cAttractions_per_ind_emp_nhb = 0.5 + +# Other Factors +cProd_school_hbw = 0 +cProd_school_hbo = 0 +cProd_school_nhb = 0.025 + +cAttractions_school_hbw = 0.35 +cAttractions_school_hbo = 0.60 +cAttractions_school_nhb = 0.025 + +# Non-home based trips generated by project households +c_nhbtgph_within_project = 0.0 +c_nhbtgph_in_to_out_project = 0.0 +c_nhbtgph_outside_project = 1.0 + +# 3 -- Trip Purpose Splits ------------------------ + +# 4 -- Auto ownership assumptions ------------------------ +cAuto_const = 0.2908 +cAuto_const_coef = 1.0 +cAuto_workers_per_hh_coef = 0.5574 +cAuto_non_workers_16_64_hh_coef = 0.3901 +cAuto_non_workers_65_hh_coef = 0.3957 +cAuto_hh_income_coef = 0.1851 +cAuto_hh_income_gt_100k_coef = -0.1865 +cAuto_hh_mfdu_coef = -0.2323 +cAuto_log_net_hu_dens_coef = -0.0654 +cAuto_log_net_retail_dens_coef = -0.0224 + +cAuto_jobs_45_trans_coef = -0.00000031721 + + +# 5 -- Models Variables ---------------------------------------------------------- + +cICPM_hbw_107 = -1.75 +cICPM_hbo_107 = -2.43 +cICPM_nhb_107 = -5.32 +cICPM_hbw_108 = None +cICPM_hbo_108 = 0.0 +cICPM_nhb_108 = 0.208 +cICPM_hbw_109 = None +cICPM_hbo_109 = 0.486 ## 12may11 +cICPM_nhb_109 = 0.468 +cICPM_hbw_110 = 0.389 +cICPM_hbo_110 = 0.399 ## 12may11 +cICPM_nhb_110 = None +cICPM_hbw_111 = None +cICPM_hbo_111 = 0.38 +cICPM_nhb_111 = None +cICPM_hbw_112 = None +cICPM_hbo_112 = 0.385 +cICPM_nhb_112 = 0.638 ##31Oct10 missed term added +cICPM_hbw_113 = -1.33 ##14Dec10 corrected constant +cICPM_hbo_113 = -0.867 +cICPM_nhb_113 = -0.237 +cICPM_hbw_114 = -0.99 +cICPM_hbo_114 = -0.59 +cICPM_nhb_114 = -0.163 + +cWTPM_hbw_121 = -5.55 +cWTPM_hbo_121 = -10.96 +cWTPM_nhb_121 = -15.09 +cWTPM_hbw_122 = None +cWTPM_hbo_122 = -0.415 +cWTPM_nhb_122 = None +cWTPM_hbw_123 = None +cWTPM_hbo_123 = 0.37 +cWTPM_nhb_123 = 0.377 +cWTPM_hbw_124 = 0.226 +cWTPM_hbo_124 = 0.219 #13May11 +cWTPM_nhb_124 = None +cWTPM_hbw_125 = None +cWTPM_hbo_125 = 0.216 +cWTPM_nhb_125 = None +cWTPM_hbw_126 = None +cWTPM_hbo_126 = 0.0 +cWTPM_nhb_126 = 0.803 +cWTPM_hbw_127 = 0.385 +cWTPM_hbo_127 = 0.45 +cWTPM_nhb_127 = 0.44 +cWTPM_hbw_128 = -1.57 +cWTPM_hbo_128 = -0.486 +cWTPM_nhb_128 = -0.281 +cWTPM_hbw_129 = -1.84 +cWTPM_hbo_129 = -0.768 +cWTPM_nhb_129 = -0.242 + +cTTPM_hbw_136 = -8.05 +cTTPM_hbo_136 = -6.08 +cTTPM_nhb_136 = -2.69 +cTTPM_hbw_137 = None +cTTPM_hbo_137 = 0.324 +cTTPM_nhb_137 = None +cTTPM_hbw_138 = 1.12 +cTTPM_hbo_138 = None +cTTPM_nhb_138 = None +cTTPM_hbw_139 = 0.209 +cTTPM_hbo_139 = None +cTTPM_nhb_139 = 0.134 +cTTPM_hbw_140 = 0.357 +cTTPM_hbo_140 = 0.467 +cTTPM_nhb_140 = None +cTTPM_hbw_141 = -1.14 +cTTPM_hbo_141 = -0.958 +cTTPM_nhb_141 = None +cTTPM_hbw_142 = -1.68 +cTTPM_hbo_142 = -1.09 +cTTPM_nhb_142 = -0.34 + +vmt_output_field_list = ['id', 'acres_gross', 'pop', 'du', 'hh', 'emp', 'final_prod_hbo', 'final_prod_hbw', 'final_prod_nhb', + 'final_attr_hbo', 'final_attr_hbw', 'final_attr_nhb', 'vmt_daily', 'vmt_daily_w_trucks', + 'vmt_daily_per_capita', 'vmt_daily_per_hh', 'vmt_annual', 'vmt_annual_w_trucks', + 'vmt_annual_per_capita', 'vmt_annual_per_hh', 'raw_trips_total', 'internal_capture_trips_total', + 'walking_trips_total', 'transit_trips_total'] \ No newline at end of file diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_one_mile_buffer_preprocess.py b/footprint/main/models/analysis_module/vmt_module/vmt_one_mile_buffer_preprocess.py new file mode 100644 index 000000000..7a692b73e --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_one_mile_buffer_preprocess.py @@ -0,0 +1,127 @@ +from footprint.main.models.config.scenario import FutureScenario +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.uf_toolbox import queue_process, report_sql_values, execute_sql, drop_table, \ + add_primary_key, count_cores, add_geom_idx, MultithreadProcess, reproject_table, add_constraint_SRID +from footprint.main.utils.utils import parse_schema_and_table + +__author__ = 'calthorpe' + + +def run_one_mile_buffers(config_entity): + #for the vmt model, this function calculates the sum of each field within a quarter mile of each input geometry + #and produces an output join table + + thread_count = count_cores() + + vmt_one_mile_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_VMT_ONE_MILE_BUFFER_FEATURE) + + if isinstance(config_entity.subclassed_config_entity, FutureScenario): + scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_FEATURE) + + else: + scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_FEATURE) + + + input_table = scenario_class.db_entity_key + input_schema = parse_schema_and_table(scenario_class._meta.db_table)[0] + + vmt_table = vmt_one_mile_class.db_entity_key + vmt_schema = parse_schema_and_table(vmt_one_mile_class._meta.db_table)[0] + + + + queue = queue_process() + + gQry = '''select pg_typeof(id) from {0}.{1} limit 2;'''.format(input_schema, input_table) + geometry_id_type = report_sql_values(gQry, 'fetchone')[0] + + pSql = '''drop function if exists one_mile_buffer( + in_id {0}, + in_wkb_geometry geometry, + OUT id {0}, + OUT emp numeric(14,4), + OUT wkb_geometry geometry + );'''.format(geometry_id_type) + + execute_sql(pSql) + + print 'Multithread running' + + gQry1 = ''' + CREATE OR REPLACE FUNCTION one_mile_buffer( + in_id {2}, + in_wkb_geometry geometry, + OUT id {2}, + OUT emp numeric(14,4), + OUT wkb_geometry geometry + ) + AS + $$ + select $1 as id, + sum(round(cast(r.emp as numeric(14,4)), 4)) as emp, + $2 as wkb_geometry + FROM {0}.{1} r WHERE st_dwithin( $2, ST_TRANSFORM(r.wkb_geometry, 3310), 1608) + $$ + COST 10000 + language SQL STABLE strict; + '''.format(input_schema, input_table, geometry_id_type) + + execute_sql(gQry1) + + drop_table('''{0}.{1}'''.format(vmt_schema, vmt_table)) + + pSql = ''' + create table {0}.{1} + ( + id serial NOT NULL, + emp numeric(14,4) NOT NULL, + wkb_geometry geometry + );'''.format(vmt_schema, vmt_table) + + execute_sql(pSql) + + #======================================================================================= + + #pass a flat list of the input table ids for the multithread process to use to split the process into tasks + id_list = scenario_class.objects.values_list('id', flat=True).order_by('id') + ##----------------------------------------------------------------------------- + #spawn a pool of threads, and pass them queue instance + + insert_sql = ''' + insert into {0}.{1} + select (f).* from (select one_mile_buffer(id, ST_TRANSFORM(wkb_geometry, 3310)) as f + from {2}.{3} + where id >= {4} and id <= {5} offset 0) s; + '''.format(vmt_schema, vmt_table, input_schema, input_table, "{0}", "{1}") + + for i in range(thread_count): + t = MultithreadProcess(queue, insert_sql) + t.setDaemon(True) + t.start() + + #populate queue with data + rows_per_thread = len(id_list) / thread_count + offset = 0 + + for i in range(thread_count): + if i == thread_count - 1: + ## last bucket gets any remainder, too + last_thread = len(id_list) - 1 + else: + last_thread = offset + rows_per_thread - 1 + + rows_to_process = { + 'start_id': id_list[offset], + 'end_id': id_list[last_thread] + } + + offset += rows_per_thread + queue.put(rows_to_process) + + #wait on the queue until everything has been processed + queue.join() + + reproject_table(vmt_schema, vmt_table, '4326') + add_primary_key(vmt_schema, vmt_table, 'id') + add_geom_idx(vmt_schema, vmt_table) + add_constraint_SRID(vmt_schema, vmt_table, '4326') diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_quarter_mile_buffer_preprocess.py b/footprint/main/models/analysis_module/vmt_module/vmt_quarter_mile_buffer_preprocess.py new file mode 100644 index 000000000..361d910c8 --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_quarter_mile_buffer_preprocess.py @@ -0,0 +1,151 @@ +from footprint.main.models.config.scenario import FutureScenario +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.uf_toolbox import queue_process, report_sql_values, execute_sql, drop_table, \ + MultithreadProcess, add_primary_key, count_cores, add_geom_idx, drop_geom_idx, drop_spatial_constraint, reproject_table, add_constraint_SRID +from footprint.main.utils.utils import parse_schema_and_table + +__author__ = 'calthorpe' + + + +def run_quarter_mile_buffers(config_entity): + #for the vmt model, this function calculates the sum of each field within a quarter mile of each input geometry + #and produces an output join table + + thread_count = count_cores() + + vmt_quarter_mile_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_VMT_QUARTER_MILE_BUFFER_FEATURE) + + if isinstance(config_entity.subclassed_config_entity, FutureScenario): + scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_FEATURE) + + else: + scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_FEATURE) + + + input_table = scenario_class.db_entity_key + input_schema = parse_schema_and_table(scenario_class._meta.db_table)[0] + + vmt_table = vmt_quarter_mile_class.db_entity_key + vmt_schema = parse_schema_and_table(vmt_quarter_mile_class._meta.db_table)[0] + + queue = queue_process() + + gQry = '''select pg_typeof(id) from {0}.{1} limit 2;'''.format(input_schema, input_table) + geometry_id_type = report_sql_values(gQry, 'fetchone')[0] + + pSql = '''drop function if exists quarter_mile_buffer( + in_id {0}, + in_wkb_geometry geometry, + OUT id {0}, + OUT acres_parcel_res numeric(14,4), + OUT acres_parcel_emp numeric(14,4), + OUT acres_parcel_mixed numeric(14,4), + OUT du numeric(14,4), + OUT pop numeric(14,4), + OUT emp numeric(14,4), + OUT emp_ret numeric(14,4), + OUT wkb_geometry geometry + );'''.format(geometry_id_type) + + execute_sql(pSql) + + print 'Multithread running' + + gQry1 = ''' + CREATE OR REPLACE FUNCTION quarter_mile_buffer( + in_id {2}, + in_wkb_geometry geometry, + OUT id {2}, + OUT acres_parcel_res numeric(14,4), + OUT acres_parcel_emp numeric(14,4), + OUT acres_parcel_mixed numeric(14,4), + OUT du numeric(14,4), + OUT pop numeric(14,4), + OUT emp numeric(14,4), + OUT emp_ret numeric(14,4), + OUT wkb_geometry geometry + ) + AS + $$ + select $1 as id, + sum(round(cast(r.acres_parcel_res as numeric(14,4)), 4)) as acres_parcel_res, + sum(round(cast(r.acres_parcel_emp as numeric(14,4)), 4)) as acres_parcel_emp, + sum(round(cast(r.acres_parcel_mixed as numeric(14,4)), 4)) as acres_parcel_mixed, + sum(round(cast(r.du as numeric(14,4)), 4)) as du, + sum(round(cast(r.pop as numeric(14,4)), 4)) as pop, + sum(round(cast(r.emp as numeric(14,4)), 4)) as emp, + sum(round(cast(r.emp_ret as numeric(14,4)), 4)) as emp_ret, + $2 as wkb_geometry + FROM {0}.{1} r WHERE st_dwithin( $2, st_transform(r.wkb_geometry, 3310), 402) + $$ + COST 10000 + language SQL STABLE strict; + '''.format(input_schema, input_table, geometry_id_type) + + execute_sql(gQry1) + + drop_table('''{0}.{1}'''.format(vmt_schema, vmt_table)) + + pSql = ''' + create table {0}.{1} + ( + id serial NOT NULL, + acres_parcel_res numeric(14,4) NOT NULL, + acres_parcel_emp numeric(14,4) NOT NULL, + acres_parcel_mixed numeric(14,4) NOT NULL, + du numeric(14,4) NOT NULL, + pop numeric(14,4) NOT NULL, + emp numeric(14,4) NOT NULL, + emp_ret numeric(14,4) NOT NULL, + wkb_geometry geometry + );'''.format(vmt_schema, vmt_table) + + execute_sql(pSql) + + #======================================================================================= + + #pass a flat list of the input table ids for the multithread process to use to split the process into tasks + id_list = scenario_class.objects.values_list('id', flat=True).order_by('id') + ##----------------------------------------------------------------------------- + #spawn a pool of threads, and pass them queue instance + + insert_sql = ''' + insert into {0}.{1} + select (f).* from (select quarter_mile_buffer(id, st_transform(wkb_geometry,3310)) as f + from {2}.{3} + where id >= {4} and id <= {5} offset 0) s; + '''.format(vmt_schema, vmt_table, input_schema, input_table, "{0}", "{1}") + + for i in range(thread_count): + t = MultithreadProcess(queue, insert_sql) + t.setDaemon(True) + t.start() + + ##--------------------------------------------------- + #populate queue with data + rows_per_thread = len(id_list) / thread_count + offset = 0 + + for i in range(thread_count): + if i == thread_count - 1: + ## last bucket gets any remainder, too + last_thread = len(id_list) - 1 + else: + last_thread = offset + rows_per_thread - 1 + + rows_to_process = { + 'start_id': id_list[offset], + 'end_id': id_list[last_thread] + } + + offset += rows_per_thread + queue.put(rows_to_process) + + #wait on the queue until everything has been processed + queue.join() + + reproject_table(vmt_schema, vmt_table, '4326') + add_primary_key(vmt_schema, vmt_table, 'id') + add_geom_idx(vmt_schema, vmt_table) + add_constraint_SRID(vmt_schema, vmt_table, '4326') diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_raw_trip_generation.py b/footprint/main/models/analysis_module/vmt_module/vmt_raw_trip_generation.py new file mode 100644 index 000000000..4854e74c8 --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_raw_trip_generation.py @@ -0,0 +1,109 @@ +from footprint.main.models.analysis_module.vmt_module.vmt_model_constants import cJobConversionRate + +__author__ = 'calthorpe' + + +def generate_raw_trips(vmt_feature): + #------------------------------------------------------ + #single family trips + #------------------------------------------------------ + if vmt_feature['du_detsf'] == 0 or vmt_feature['du_detsf'] is None: + single_family_trips = 0 + else: + single_family_trips = vmt_feature['du_detsf'] * 9.57 #mk9 + + vmt_feature['raw_trips_sf'] = int(round(single_family_trips)) + + #------------------------------------------------------ + #multifamily trips + #------------------------------------------------------ + if vmt_feature['du_mf2to4'] == 0 or vmt_feature['du_mf2to4'] is None: + multifamily_2to4_trips = 0.0 + else: + multifamily_2to4_trips = vmt_feature['du_mf2to4'] * 6.65 #mk9 + + if vmt_feature['du_mf5p'] == 0 or vmt_feature['du_mf5p'] is None: + multifamily_5plus_trips = 0.0 + else: + multifamily_5plus_trips = vmt_feature['du_mf5p'] * 4.18 #mk9 + + vmt_feature['raw_trips_mf24'] = int(round(multifamily_2to4_trips)) + vmt_feature['raw_trips_mf5'] = int(round(multifamily_5plus_trips)) + + #------------------------------------------------------ + #retail trips + #------------------------------------------------------ + if vmt_feature['emp_retail'] == 0 or vmt_feature['emp_retail'] is None: + retail_trips = 0.0 + else: + retail_trips = (vmt_feature['emp_retail'] / cJobConversionRate) * 42.94 #mk9 + + vmt_feature['raw_trips_retail'] = int(round(retail_trips)) + + #------------------------------------------------------ + #restaurant trips + #------------------------------------------------------ + if vmt_feature['emp_restaccom'] == 0 or vmt_feature['emp_restaccom'] is None: + restaurant_trips = 0.0 + else: + restaurant_trips = (vmt_feature['emp_restaccom'] / 2.0) * 75.0 + + vmt_feature['raw_trips_restaurant'] = int(round(restaurant_trips)) + + #------------------------------------------------------ + #arts/entertainment trips + #------------------------------------------------------ + if vmt_feature['emp_arts_entertainment'] == 0 or vmt_feature['emp_arts_entertainment'] is None: + arts_entertainment_trips = 0.0 + else: + arts_entertainment_trips = (vmt_feature['emp_arts_entertainment'] / 2.0) * 20.0 + + vmt_feature['raw_trips_entertainment'] = int(round(arts_entertainment_trips)) + + #------------------------------------------------------ + #Office trips + #------------------------------------------------------ + if vmt_feature['emp_office'] == 0 or vmt_feature['emp_office'] is None: + office_trips = 0.0 + else: + office_trips = vmt_feature['emp_office'] * 3.32 + + vmt_feature['raw_trips_office'] = int(round(office_trips)) + + #------------------------------------------------------ + #Public Administration Trips + #------------------------------------------------------ + if vmt_feature['emp_public'] == 0 or vmt_feature['emp_public'] is None: + public_trips = 0.0 + else: + public_trips = vmt_feature['emp_public'] * 3.32 #mk9 + + vmt_feature['raw_trips_public'] = int(round(public_trips)) + + #------------------------------------------------------ + #Industrial trips + #------------------------------------------------------ + if vmt_feature['emp_industry'] == 0 or vmt_feature['emp_industry'] is None: + industrial_trips = 0.0 + else: + industrial_trips = vmt_feature['emp_industry'] * 3.02 #mk9 + + vmt_feature['raw_trips_industry'] = int(round(industrial_trips)) + + #------------------------------------------------------ + #K-12 School trips + #------------------------------------------------------ + + if vmt_feature['du_detsf'] + vmt_feature['du_mf2to4'] + vmt_feature['du_mf5p'] == 0 or vmt_feature['du_detsf'] + \ + vmt_feature['du_mf2to4'] + vmt_feature[ + 'du_mf5p'] is None: + education_trips = 0.0 + else: + education_trips = ((vmt_feature['du_detsf'] * 9.57) + (vmt_feature['du_mf2to4'] * 6.65) + ( + vmt_feature['du_mf5p'] * 4.18)) * 0.097 + + vmt_feature['raw_trips_k12'] = int(round(education_trips)) + vmt_feature['raw_trips_h_ed'] = 0 + + return vmt_feature + diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_trip_purpose_splits.py b/footprint/main/models/analysis_module/vmt_module/vmt_trip_purpose_splits.py new file mode 100644 index 000000000..cf9efddc5 --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_trip_purpose_splits.py @@ -0,0 +1,232 @@ +__author__ = 'calthorpe' + +from footprint.main.models.analysis_module.vmt_module.vmt_model_constants import cProd_perc_per_hh, cProd_perc_hbw, cAttractions_per_hh_hbw, cAttractions_per_ret_emp_hbw, cAttractions_per_service_emp_hbw, cAttractions_per_ind_emp_hbw, cProd_perc_hbo, cAttractions_per_hh_hbo, cAttractions_per_ret_emp_hbo, cAttractions_per_service_emp_hbo, cAttractions_per_ind_emp_hbo, cProd_perc_nhb, cAttractions_per_hh_nhb, cAttractions_per_ret_emp_nhb, cAttractions_per_service_emp_nhb, cAttractions_per_ind_emp_nhb, c_nhbtgph_within_project, c_nhbtgph_in_to_out_project, cProd_school_hbw, cProd_school_hbo, cProd_school_nhb, cAttractions_school_hbw, cAttractions_school_hbo, cAttractions_school_nhb + + +##------------------------------- +def calculate_trip_purpose_splits(vmt_feature): + tDTPD_residential = vmt_feature['du_detsf'] + vmt_feature['du_mf'] + ##-- + tDTPD_retail = vmt_feature['emp_retail'] + vmt_feature['emp_restaccom'] + vmt_feature['emp_arts_entertainment'] + ##-- + tDTPD_office = vmt_feature['emp_office'] + vmt_feature['emp_public'] + tDTPD_industrial_other = vmt_feature['emp_industry'] + tDTPD_school = 0 + 0 # TODO School trips k-12, higher ed + + B64 = cProd_perc_per_hh + B69 = float(tDTPD_residential) + B70 = float(tDTPD_retail) + B71 = float(tDTPD_office) + B72 = float(tDTPD_industrial_other) + B73 = 0.0 # TODO Schools + + C33 = cProd_perc_hbw + C36 = cAttractions_per_hh_hbw + C37 = cAttractions_per_ret_emp_hbw + C38 = cAttractions_per_service_emp_hbw + C39 = cAttractions_per_ind_emp_hbw + D33 = cProd_perc_hbo + D36 = cAttractions_per_hh_hbo + D37 = cAttractions_per_ret_emp_hbo + D38 = cAttractions_per_service_emp_hbo + D39 = cAttractions_per_ind_emp_hbo + E33 = cProd_perc_nhb + E36 = cAttractions_per_hh_nhb + E37 = cAttractions_per_ret_emp_nhb + E38 = cAttractions_per_service_emp_nhb + E39 = cAttractions_per_ind_emp_nhb + + B50 = c_nhbtgph_within_project + B51 = c_nhbtgph_in_to_out_project + + K69 = vmt_feature['raw_trips_sf'] + vmt_feature['raw_trips_mf24'] + vmt_feature['raw_trips_mf5'] + K70 = vmt_feature['raw_trips_retail'] + vmt_feature['raw_trips_restaurant'] + vmt_feature['raw_trips_entertainment'] + K71 = vmt_feature['raw_trips_office'] + vmt_feature['raw_trips_public'] + K72 = vmt_feature['raw_trips_industry'] + K73 = vmt_feature['raw_trips_k12'] + vmt_feature['raw_trips_h_ed'] + + #-------------- + tNHCRP_PROD_residential_hbw = B69 * B64 * C33 + tNHCRP_PROD_residential_hbo = B69 * B64 * D33 + tNHCRP_PROD_residential_nhb = B69 * B64 * E33 * (B50 + B51) + + tNHCRP_ATTR_residential_hbw = B69 * C36 + tNHCRP_ATTR_residential_hbo = B69 * D36 + tNHCRP_ATTR_residential_nhb = B69 * E36 + + #-------------------------------------------------------- + + tNHCRP_PROD_retail_hbw = 0 + tNHCRP_PROD_retail_hbo = 0 + tNHCRP_PROD_retail_nhb = (B70 * E37) / 2 + + tNHCRP_ATTR_retail_hbw = B70 * C37 + tNHCRP_ATTR_retail_hbo = B70 * D37 + tNHCRP_ATTR_retail_nhb = (B70 * E37) / 2 + + #-------------- + tNHCRP_PROD_office_hbw = 0 + tNHCRP_PROD_office_hbo = 0 + tNHCRP_PROD_office_nhb = (B71 * E38) / 2 + + tNHCRP_ATTR_office_hbw = B71 * C38 + tNHCRP_ATTR_office_hbo = B71 * D38 + tNHCRP_ATTR_office_nhb = (B71 * E38) / 2 + + #-------------- + tNHCRP_PROD_industrial_hbw = 0 + tNHCRP_PROD_industrial_hbo = 0 + tNHCRP_PROD_industrial_nhb = (B72 * E39) / 2 + + tNHCRP_ATTR_industrial_hbw = B72 * C39 + tNHCRP_ATTR_industrial_hbo = B72 * D39 + tNHCRP_ATTR_industrial_nhb = (B72 * E39) / 2 + + #-------------- + ## Note: no NCHRP stats for school trips + + #---------------------------------------- + tSum = tNHCRP_PROD_residential_hbw + tNHCRP_PROD_residential_hbo + tNHCRP_PROD_residential_nhb + \ + tNHCRP_ATTR_residential_hbw + tNHCRP_ATTR_residential_hbo + tNHCRP_ATTR_residential_nhb + + if tSum > 0: + tITE_PROD_residential_hbw = (K69 * tNHCRP_PROD_residential_hbw) / tSum + tITE_PROD_residential_hbo = (K69 * tNHCRP_PROD_residential_hbo) / tSum + tITE_PROD_residential_nhb = (K69 * tNHCRP_PROD_residential_nhb) / tSum + tITE_ATTR_residential_hbw = (K69 * tNHCRP_ATTR_residential_hbw) / tSum + tITE_ATTR_residential_hbo = (K69 * tNHCRP_ATTR_residential_hbo) / tSum + tITE_ATTR_residential_nhb = (K69 * tNHCRP_ATTR_residential_nhb) / tSum + else: + tITE_PROD_residential_hbw = 0 + tITE_PROD_residential_hbo = 0 + tITE_PROD_residential_nhb = 0 + tITE_ATTR_residential_hbw = 0 + tITE_ATTR_residential_hbo = 0 + tITE_ATTR_residential_nhb = 0 + + #---------------------------------------- + tSum = tNHCRP_PROD_retail_hbw + tNHCRP_PROD_retail_hbo + tNHCRP_PROD_retail_nhb + \ + tNHCRP_ATTR_retail_hbw + tNHCRP_ATTR_retail_hbo + tNHCRP_ATTR_retail_nhb + + if tSum > 0: + tITE_PROD_retail_hbw = (K70 * tNHCRP_PROD_retail_hbw) / tSum + tITE_PROD_retail_hbo = (K70 * tNHCRP_PROD_retail_hbo) / tSum + tITE_PROD_retail_nhb = (K70 * tNHCRP_PROD_retail_nhb) / tSum + tITE_ATTR_retail_hbw = (K70 * tNHCRP_ATTR_retail_hbw) / tSum + tITE_ATTR_retail_hbo = (K70 * tNHCRP_ATTR_retail_hbo) / tSum + tITE_ATTR_retail_nhb = (K70 * tNHCRP_ATTR_retail_nhb) / tSum + else: + tITE_PROD_retail_hbw = 0 + tITE_PROD_retail_hbo = 0 + tITE_PROD_retail_nhb = 0 + tITE_ATTR_retail_hbw = 0 + tITE_ATTR_retail_hbo = 0 + tITE_ATTR_retail_nhb = 0 + + #---------------------------------------- + + tSum = tNHCRP_PROD_office_hbw + tNHCRP_PROD_office_hbo + tNHCRP_PROD_office_nhb + \ + tNHCRP_ATTR_office_hbw + tNHCRP_ATTR_office_hbo + tNHCRP_ATTR_office_nhb + + if tSum > 0: + tITE_PROD_office_hbw = (K71 * tNHCRP_PROD_office_hbw) / tSum + tITE_PROD_office_hbo = (K71 * tNHCRP_PROD_office_hbo) / tSum + tITE_PROD_office_nhb = (K71 * tNHCRP_PROD_office_nhb) / tSum + tITE_ATTR_office_hbw = (K71 * tNHCRP_ATTR_office_hbw) / tSum + tITE_ATTR_office_hbo = (K71 * tNHCRP_ATTR_office_hbo) / tSum + tITE_ATTR_office_nhb = (K71 * tNHCRP_ATTR_office_nhb) / tSum + else: + tITE_PROD_office_hbw = 0 + tITE_PROD_office_hbo = 0 + tITE_PROD_office_nhb = 0 + tITE_ATTR_office_hbw = 0 + tITE_ATTR_office_hbo = 0 + tITE_ATTR_office_nhb = 0 + + #---------------------------------------- + + tSum = tNHCRP_PROD_industrial_hbw + tNHCRP_PROD_industrial_hbo + tNHCRP_PROD_industrial_nhb + \ + tNHCRP_ATTR_industrial_hbw + tNHCRP_ATTR_industrial_hbo + tNHCRP_ATTR_industrial_nhb + + if tSum > 0: + tITE_PROD_industrial_hbw = (K72 * tNHCRP_PROD_industrial_hbw) / tSum + tITE_PROD_industrial_hbo = (K72 * tNHCRP_PROD_industrial_hbo) / tSum + tITE_PROD_industrial_nhb = (K72 * tNHCRP_PROD_industrial_nhb) / tSum + tITE_ATTR_industrial_hbw = (K72 * tNHCRP_ATTR_industrial_hbw) / tSum + tITE_ATTR_industrial_hbo = (K72 * tNHCRP_ATTR_industrial_hbo) / tSum + tITE_ATTR_industrial_nhb = (K72 * tNHCRP_ATTR_industrial_nhb) / tSum + else: + tITE_PROD_industrial_hbw = 0 + tITE_PROD_industrial_hbo = 0 + tITE_PROD_industrial_nhb = 0 + tITE_ATTR_industrial_hbw = 0 + tITE_ATTR_industrial_hbo = 0 + tITE_ATTR_industrial_nhb = 0 + + #-------------- + tITE_PROD_school_hbw = K73 * cProd_school_hbw + tITE_PROD_school_hbo = K73 * cProd_school_hbo + tITE_PROD_school_nhb = K73 * cProd_school_nhb + tITE_ATTR_school_hbw = K73 * cAttractions_school_hbw + tITE_ATTR_school_hbo = K73 * cAttractions_school_hbo + tITE_ATTR_school_nhb = K73 * cAttractions_school_nhb + + #-------------- + tITE_PROD_hbw_all = tITE_PROD_residential_hbw + tITE_PROD_retail_hbw + tITE_PROD_office_hbw + \ + tITE_PROD_industrial_hbw + tITE_PROD_school_hbw + + tITE_PROD_hbo_all = tITE_PROD_residential_hbo + tITE_PROD_retail_hbo + tITE_PROD_office_hbo + \ + tITE_PROD_industrial_hbo + tITE_PROD_school_hbo + + tITE_PROD_nhb_all = tITE_PROD_residential_nhb + tITE_PROD_retail_nhb + tITE_PROD_office_nhb + \ + tITE_PROD_industrial_nhb + tITE_PROD_school_nhb + + tITE_ATTR_hbw_all = tITE_ATTR_residential_hbw + tITE_ATTR_retail_hbw + tITE_ATTR_office_hbw + \ + tITE_ATTR_industrial_hbw + tITE_ATTR_school_hbw + + tITE_ATTR_hbo_all = tITE_ATTR_residential_hbo + tITE_ATTR_retail_hbo + tITE_ATTR_office_hbo + \ + tITE_ATTR_industrial_hbo + tITE_ATTR_school_hbo + + tITE_ATTR_nhb_all = tITE_ATTR_residential_nhb + tITE_ATTR_retail_nhb + tITE_ATTR_office_nhb + \ + tITE_ATTR_industrial_nhb + tITE_ATTR_school_nhb + + #-------------- + tTP_residential_hbw_all = tITE_PROD_residential_hbw + tITE_ATTR_residential_hbw + tTP_residential_hbo_all = tITE_PROD_residential_hbo + tITE_ATTR_residential_hbo + tTP_residential_nhb_all = tITE_PROD_residential_nhb + tITE_ATTR_residential_nhb + + tTP_retail_hbw_all = tITE_PROD_retail_hbw + tITE_ATTR_retail_hbw + tTP_retail_hbo_all = tITE_PROD_retail_hbo + tITE_ATTR_retail_hbo + tTP_retail_nhb_all = tITE_PROD_retail_nhb + tITE_ATTR_retail_nhb + + tTP_office_hbw_all = tITE_PROD_office_hbw + tITE_ATTR_office_hbw + tTP_office_hbo_all = tITE_PROD_office_hbo + tITE_ATTR_office_hbo + tTP_office_nhb_all = tITE_PROD_office_nhb + tITE_ATTR_office_nhb + + tTP_industrial_hbw_all = tITE_PROD_industrial_hbw + tITE_ATTR_industrial_hbw + tTP_industrial_hbo_all = tITE_PROD_industrial_hbo + tITE_ATTR_industrial_hbo + tTP_industrial_nhb_all = tITE_PROD_industrial_nhb + tITE_ATTR_industrial_nhb + + tTP_school_hbw_all = tITE_PROD_school_hbw + tITE_ATTR_school_hbw + tTP_school_hbo_all = tITE_PROD_school_hbo + tITE_ATTR_school_hbo + tTP_school_nhb_all = tITE_PROD_school_nhb + tITE_ATTR_school_nhb + + #-------------- + tTP_hbw_all = tTP_residential_hbw_all + tTP_retail_hbw_all + tTP_office_hbw_all + tTP_industrial_hbw_all + tTP_school_hbw_all + tTP_hbo_all = tTP_residential_hbo_all + tTP_retail_hbo_all + tTP_office_hbo_all + tTP_industrial_hbo_all + tTP_school_hbo_all + tTP_nhb_all = tTP_residential_nhb_all + tTP_retail_nhb_all + tTP_office_nhb_all + tTP_industrial_nhb_all + tTP_school_nhb_all + + ##========================================== + + vmt_feature['trips_hbw'] = tTP_hbw_all + vmt_feature['trips_hbo'] = tTP_hbo_all + vmt_feature['trips_nhb'] = tTP_nhb_all + vmt_feature['ite_attr_hbw'] = tITE_ATTR_hbw_all + vmt_feature['ite_attr_hbo'] = tITE_ATTR_hbo_all + vmt_feature['ite_attr_nhb'] = tITE_ATTR_nhb_all + vmt_feature['ite_prod_hbw'] = tITE_PROD_hbw_all + vmt_feature['ite_prod_hbo'] = tITE_PROD_hbo_all + vmt_feature['ite_prod_nhb'] = tITE_PROD_nhb_all + + return vmt_feature + diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_variable_buffer_preprocess.py b/footprint/main/models/analysis_module/vmt_module/vmt_variable_buffer_preprocess.py new file mode 100644 index 000000000..c2c844def --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_variable_buffer_preprocess.py @@ -0,0 +1,301 @@ +from footprint.main.lib.functions import flatten +from footprint.main.models.config.scenario import FutureScenario +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.uf_toolbox import queue_process, report_sql_values, execute_sql, drop_table, \ + MultithreadProcess, add_primary_key, count_cores, add_geom_idx, add_constraint_SRID +from footprint.main.utils.utils import parse_schema_and_table + +__author__ = 'calthorpe' + + + +def run_variable_buffers(config_entity): + #for the vmt model, this function calculates the sum of each field within a variable buffer of each input geometry + #and produces an output join table + + thread_count = count_cores() + + + if isinstance(config_entity.subclassed_config_entity, FutureScenario): + scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_FEATURE) + demographics_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_END_STATE_DEMOGRAPHIC_FEATURE) + vmt_trip_lengths_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_VMT_FUTURE_TRIP_LENGTHS_FEATURE) + + else: + scenario_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_FEATURE) + demographics_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_BASE_DEMOGRAPHIC_FEATURE) + vmt_trip_lengths_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_VMT_BASE_TRIP_LENGTHS_FEATURE) + + vmt_variable_class = config_entity.feature_class_of_db_entity_key(Keys.DB_ABSTRACT_VMT_VARIABLE_BUFFER_FEATURE) + + + input_table = scenario_class.db_entity_key + input_demographic_table = demographics_class.db_entity_key + input_schema = parse_schema_and_table(scenario_class._meta.db_table)[0] + + + vmt_table = vmt_variable_class.db_entity_key + vmt_schema = parse_schema_and_table(vmt_variable_class._meta.db_table)[0] + + + project_schema = parse_schema_and_table(vmt_trip_lengths_class._meta.db_table)[0] + vmt_trips_table = vmt_trip_lengths_class.db_entity_key + + queue = queue_process() + + drop_table('''{0}.{1}_intersect'''.format(vmt_schema, vmt_table)) + pSql = ''' + create table {4}.{5}_intersect + as select + a.id as id, + b.id as trip_id, + a.wkb_geometry + from {0}.{1} a, {2}.{3} b + where st_intersects(st_centroid(a.wkb_geometry), b.wkb_geometry); + + '''.format(input_schema, input_table, project_schema, vmt_trips_table, vmt_schema, vmt_table) + + execute_sql(pSql) + + + drop_table('''{0}.{1}_grouped'''.format(vmt_schema, vmt_table)) + + pSql = ''' + create table {5}.{6}_grouped + as select + trip_id as id, d.wkb_geometry, + sum(round(cast(b.acres_parcel_res as numeric(14,4)), 4)) as acres_parcel_res, + sum(round(cast(b.acres_parcel_emp as numeric(14,4)), 4)) as acres_parcel_emp, + sum(round(cast(b.acres_parcel_mixed as numeric(14,4)), 4)) as acres_parcel_mixed, + sum(round(cast(b.du as numeric(14,4)), 4)) as du, + sum(round(cast(b.pop as numeric(14,4)), 4)) as pop, + sum(round(cast(b.emp as numeric(14,4)), 4)) as emp, + sum(round(cast(b.emp_ret as numeric(14,4)), 4)) as emp_ret, + sum(round(cast(b.hh as numeric(14,4)), 4)) as hh, + sum(round(cast(b.du_mf as numeric(14,4)), 4)) as du_mf, + sum(round(cast(c.hh_inc_00_10 as numeric(14,4)), 4)) as hh_inc_00_10, + sum(round(cast(c.hh_inc_10_20 as numeric(14,4)), 4)) as hh_inc_10_20, + sum(round(cast(c.hh_inc_20_30 as numeric(14,4)), 4)) as hh_inc_20_30, + sum(round(cast(c.hh_inc_30_40 as numeric(14,4)), 4)) as hh_inc_30_40, + sum(round(cast(c.hh_inc_40_50 as numeric(14,4)), 4)) as hh_inc_40_50, + sum(round(cast(c.hh_inc_50_60 as numeric(14,4)), 4)) as hh_inc_50_60, + sum(round(cast(c.hh_inc_60_75 as numeric(14,4)), 4)) as hh_inc_60_75, + sum(round(cast(c.hh_inc_75_100 as numeric(14,4)), 4)) as hh_inc_75_100, + sum(round(cast(c.hh_inc_100_125 + c.hh_inc_125_150 + c.hh_inc_150_200 + c.hh_inc_200p as numeric(14,4)), 4)) as hh_inc_100p, + sum(round(cast(c.pop_employed as numeric(14,4)), 4)) as pop_employed, + sum(round(cast(c.pop_age16_up as numeric(14,4)), 4)) as pop_age16_up, + sum(round(cast(c.pop_age65_up as numeric(14,4)), 4)) as pop_age65_up + + from {5}.{6}_intersect a + left join {0}.{1} b on a.id = b.id + left join {0}.{2} c on a.id = c.id + left join {3}.{4} d on a.trip_id = d.id + group by trip_id, d.wkb_geometry; + '''.format(input_schema, input_table, input_demographic_table, project_schema, vmt_trips_table, vmt_schema, + vmt_table) + + execute_sql(pSql) + + add_geom_idx(vmt_schema, '{0}_grouped'.format(vmt_table)) + add_primary_key(vmt_schema, '{0}_grouped'.format(vmt_table), 'id') + + + gQry = '''select pg_typeof(id) from {0}.{1}_grouped limit 1;'''.format(vmt_schema, vmt_table) + geometry_id_type = report_sql_values(gQry, 'fetchone')[0] + + pSql = '''drop function if exists variable_distance_buffers( + in_id {0}, + in_distance numeric(14,4), + in_geometry geometry, + OUT id {0}, + OUT distance numeric(14,4), + OUT acres_parcel_res numeric(14,4), + OUT acres_parcel_emp numeric(14,4), + OUT acres_parcel_mixed numeric(14,4), + OUT du numeric(14,4), + OUT pop numeric(14,4), + OUT emp numeric(14,4), + OUT emp_ret numeric(14,4), + OUT hh numeric(14,4), + OUT du_mf numeric(14,4), + OUT hh_inc_00_10 numeric(14,4), + OUT hh_inc_10_20 numeric(14,4), + OUT hh_inc_20_30 numeric(14,4), + OUT hh_inc_30_40 numeric(14,4), + OUT hh_inc_40_50 numeric(14,4), + OUT hh_inc_50_60 numeric(14,4), + OUT hh_inc_60_75 numeric(14,4), + OUT hh_inc_75_100 numeric(14,4), + OUT hh_inc_100p numeric(14,4), + OUT pop_employed numeric(14,4), + OUT pop_age16_up numeric(14,4), + OUT pop_age65_up numeric(14,4) + );'''.format(geometry_id_type) + + execute_sql(pSql) + + print 'Multithread running' + + gQry1 = ''' + CREATE OR REPLACE FUNCTION variable_distance_buffers( + in_id {0}, + in_distance numeric(14,4), + in_geometry geometry, + OUT id {0}, + OUT distance numeric(14,4), + OUT acres_parcel_res numeric(14,4), + OUT acres_parcel_emp numeric(14,4), + OUT acres_parcel_mixed numeric(14,4), + OUT du numeric(14,4), + OUT pop numeric(14,4), + OUT emp numeric(14,4), + OUT emp_ret numeric(14,4), + OUT hh numeric(14,4), + OUT du_mf numeric(14,4), + OUT hh_inc_00_10 numeric(14,4), + OUT hh_inc_10_20 numeric(14,4), + OUT hh_inc_20_30 numeric(14,4), + OUT hh_inc_30_40 numeric(14,4), + OUT hh_inc_40_50 numeric(14,4), + OUT hh_inc_50_60 numeric(14,4), + OUT hh_inc_60_75 numeric(14,4), + OUT hh_inc_75_100 numeric(14,4), + OUT hh_inc_100p numeric(14,4), + OUT pop_employed numeric(14,4), + OUT pop_age16_up numeric(14,4), + OUT pop_age65_up numeric(14,4) + ) + AS + $$ + select $1 as id, + $2 as distance, + sum(round(cast(r.acres_parcel_res as numeric(14,4)), 4)) as acres_parcel_res, + sum(round(cast(r.acres_parcel_emp as numeric(14,4)), 4)) as acres_parcel_emp, + sum(round(cast(r.acres_parcel_mixed as numeric(14,4)), 4)) as acres_parcel_mixed, + sum(round(cast(r.du as numeric(14,4)), 4)) as du, + sum(round(cast(r.pop as numeric(14,4)), 4)) as pop, + sum(round(cast(r.emp as numeric(14,4)), 4)) as emp, + sum(round(cast(r.emp_ret as numeric(14,4)), 4)) as emp_ret, + sum(round(cast(r.hh as numeric(14,4)), 4)) as hh, + sum(round(cast(r.du_mf as numeric(14,4)), 4)) as du_mf, + sum(round(cast(r.hh_inc_00_10 as numeric(14,4)), 4)) as hh_inc_00_10, + sum(round(cast(r.hh_inc_10_20 as numeric(14,4)), 4)) as hh_inc_10_20, + sum(round(cast(r.hh_inc_20_30 as numeric(14,4)), 4)) as hh_inc_20_30, + sum(round(cast(r.hh_inc_30_40 as numeric(14,4)), 4)) as hh_inc_30_40, + sum(round(cast(r.hh_inc_40_50 as numeric(14,4)), 4)) as hh_inc_40_50, + sum(round(cast(r.hh_inc_50_60 as numeric(14,4)), 4)) as hh_inc_50_60, + sum(round(cast(r.hh_inc_60_75 as numeric(14,4)), 4)) as hh_inc_60_75, + sum(round(cast(r.hh_inc_75_100 as numeric(14,4)), 4)) as hh_inc_75_100, + sum(round(cast(r.hh_inc_100p as numeric(14,4)), 4)) as hh_inc_100p, + sum(round(cast(r.pop_employed as numeric(14,4)), 4)) as pop_employed, + sum(round(cast(r.pop_age16_up as numeric(14,4)), 4)) as pop_age16_up, + sum(round(cast(r.pop_age65_up as numeric(14,4)), 4)) as pop_age65_up + FROM {1}.{2}_grouped r WHERE st_dwithin( $3, ST_TRANSFORM(r.wkb_geometry, 3310), $2) + $$ + COST 10000 + language SQL STABLE strict; + '''.format(geometry_id_type, vmt_schema, vmt_table) + + execute_sql(gQry1) + + drop_table('''{0}.{1}_tmp'''.format(vmt_schema, vmt_table)) + + + pSql = ''' + create table {0}.{1}_tmp ( + id serial NOT NULL, + distance numeric(14,4) NOT NULL, + acres_parcel_res numeric(14,4) NOT NULL, + acres_parcel_emp numeric(14,4) NOT NULL, + acres_parcel_mixed numeric(14,4) NOT NULL, + du numeric(14,4) NOT NULL, + pop numeric(14,4) NOT NULL, + emp numeric(14,4) NOT NULL, + emp_ret numeric(14,4) NOT NULL, + hh numeric(14,4) NOT NULL, + du_mf numeric(14,4) NOT NULL, + hh_inc_00_10 numeric(14,4) NOT NULL, + hh_inc_10_20 numeric(14,4) NOT NULL, + hh_inc_20_30 numeric(14,4) NOT NULL, + hh_inc_30_40 numeric(14,4) NOT NULL, + hh_inc_40_50 numeric(14,4) NOT NULL, + hh_inc_50_60 numeric(14,4) NOT NULL, + hh_inc_60_75 numeric(14,4) NOT NULL, + hh_inc_75_100 numeric(14,4) NOT NULL, + hh_inc_100p numeric(14,4) NOT NULL, + pop_employed numeric(14,4) NOT NULL, + pop_age16_up numeric(14,4) NOT NULL, + pop_age65_up numeric(14,4) NOT NULL + );'''.format(vmt_schema, vmt_table) + + execute_sql(pSql) + + #======================================================================================= + + #pass a flat list of the input table ids for the multithread process to use to split the process into tasks + pSql = 'select id from {0}.{1}_grouped order by id'''.format(vmt_schema, vmt_table) + id_list = flatten(report_sql_values(pSql, 'fetchall')) + + if len(id_list) < 2000: + thread_count = 1 + else: + thread_count = count_cores() + + insert_sql = ''' + insert into {0}.{1}_tmp + select (f).* from (select variable_distance_buffers(c.id, cast(c.attractions_hbw * 1609.0 as numeric(14,4)), ST_TRANSFORM(c.wkb_geometry, 3310)) as f + from (select a.id, a.attractions_hbw, a.wkb_geometry from {2}.{3} a, {0}.{1}_grouped b where a.id = b.id) as c + where c.id >= {4} and c.id <= {5}) as s; + '''.format(vmt_schema, vmt_table, project_schema, vmt_trips_table, "{0}", "{1}") + + for i in range(thread_count): + t = MultithreadProcess(queue, insert_sql) + t.setDaemon(True) + t.start() + + ##--------------------------------------------------- + #populate queue with data + rows_per_thread = len(id_list) / thread_count + offset = 0 + + for i in range(thread_count): + if i == thread_count - 1: + ## last bucket gets any remainder, too + last_thread = len(id_list) - 1 + else: + last_thread = offset + rows_per_thread - 1 + + rows_to_process = { + 'start_id': id_list[offset], + 'end_id': id_list[last_thread] + } + + offset += rows_per_thread + queue.put(rows_to_process) + + #wait on the queue until everything has been processed + queue.join() + + drop_table('''{0}.{1}'''.format(vmt_schema, vmt_table)) + pSql = ''' + create table {0}.{1} + as select + a.id, distance, acres_parcel_res, acres_parcel_emp, acres_parcel_mixed, + du, pop, emp, emp_ret, hh, du_mf, hh_inc_00_10, hh_inc_10_20, + hh_inc_20_30, hh_inc_30_40, hh_inc_40_50, hh_inc_50_60, hh_inc_60_75, + hh_inc_75_100, hh_inc_100p, pop_employed, pop_age16_up, pop_age65_up, productions_hbw, + productions_hbo, productions_nhb, attractions_hbw, attractions_hbo, attractions_nhb, + emp_30min_transit, emp_45min_transit, a.wkb_geometry + from {0}.{1}_intersect a + left join {0}.{1}_tmp b on a.trip_id = b.id + left join {2}.{3} c on a.trip_id = c.id + '''.format(vmt_schema, vmt_table, project_schema, vmt_trips_table) + execute_sql(pSql) + + add_primary_key(vmt_schema, vmt_table, 'id') + add_geom_idx(vmt_schema, vmt_table) + add_constraint_SRID(vmt_schema, vmt_table, '4326') + + drop_table('''{0}.{1}_intersect'''.format(vmt_schema, vmt_table)) + drop_table('''{0}.{1}_grouped'''.format(vmt_schema, vmt_table)) + drop_table('''{0}.{1}_tmp'''.format(vmt_schema, vmt_table)) \ No newline at end of file diff --git a/footprint/main/models/analysis_module/vmt_module/vmt_write_results_to_database.py b/footprint/main/models/analysis_module/vmt_module/vmt_write_results_to_database.py new file mode 100644 index 000000000..f53744e64 --- /dev/null +++ b/footprint/main/models/analysis_module/vmt_module/vmt_write_results_to_database.py @@ -0,0 +1,57 @@ +import os +from footprint.main.models.analysis_module.vmt_module.vmt_model_constants import vmt_output_field_list +from footprint.main.utils.uf_toolbox import drop_table, execute_sql, copy_from_text_to_db, create_sql_calculations + + +__author__ = 'calthorpe' + +##----------------------------------------------------------- +def write_vmt_results_to_database(options, vmt_output_list): + + drop_table('{0}.{1}'.format(options['vmt_schema'], options['vmt_result_table'])) + + attribute_list = filter(lambda x: x != 'id', vmt_output_field_list) + output_field_syntax = 'id int' + create_sql_calculations(attribute_list, ', {0} numeric(14, 4)') + + pSql = ''' + create table {0}.{1} ({2});'''.format(options['vmt_schema'], options['vmt_result_table'], output_field_syntax) + execute_sql(pSql) + + if os.path.exists('/tmp/vmt_out_tmp'): + os.remove('/tmp/vmt_out_tmp') + + vmt_output_text_file_path = open("/tmp/vmt_out_tmp", "w") + + for row in vmt_output_list: + stringrow = [] + for item in row: + if isinstance(item, int): + stringrow.append(str(item)) + else: + stringrow.append(str(round(item, 4))) + vmt_output_text_file_path.write("\t".join(stringrow,) + "\n") + + vmt_output_text_file_path.close() + + ##--------------------------- + vmt_output_text_file_path = open("/tmp/vmt_out_tmp", 'r') + + copy_from_text_to_db(vmt_output_text_file_path, '{0}.{1}'.format(options['vmt_schema'], + options['vmt_result_table'])) + ##--------------------------- + pSql = '''alter table {0}.{1} add column wkb_geometry geometry; + '''.format(options['vmt_schema'], options['vmt_result_table']) + execute_sql(pSql) + + pSql = '''update {0}.{1} b set + wkb_geometry = a.wkb_geometry + from (select id, wkb_geometry from {2}.{3}) a + where cast(a.id as int) = cast(b.id as int); + '''.format(options['vmt_schema'], options['vmt_result_table'], options['input_schema'], options['input_table']) + + execute_sql(pSql) + + + + + diff --git a/footprint/main/models/application_initialization.py b/footprint/main/models/application_initialization.py new file mode 100644 index 000000000..07e427a0f --- /dev/null +++ b/footprint/main/models/application_initialization.py @@ -0,0 +1,198 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import logging +from django.contrib.gis.geos import MultiPolygon, Polygon +from django.conf import settings +from footprint.main.initialization.data_provider import DataProvider +from footprint.client.configuration.fixture import ConfigEntitiesFixture, InitFixture +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.models import Project +from footprint.main.models.built_form.placetype_component import PlacetypeComponentCategory +from footprint.main.models.keys.keys import Keys +from footprint.main.models.sort_type import SortType +import footprint.main.models as models + +from footprint.main.models.signals import initialize_media, initialize_presentations +from footprint.main.models.config.global_config import GlobalConfig +from footprint.main.models.config.interest import Interest +from footprint.main.models.built_form.building_use_definition import BuildingUseDefinition + +from footprint.main.models.database.information_schema import SouthMigrationHistory +from footprint.main.publishing.config_entity_publishing import post_save_config_entity_initial + +# TODO Move this to publishers/application_publishing.py + +__author__ = 'calthorpe_associates' +logger = logging.getLogger(__name__) + + +def application_initialization(**kwargs): + """ + Initialize or sync the application + :param kwargs: 'limit_to_classes' as an array of ConfigEntity classes to limit processing to those + """ + + # Initialize lookup table data + if SouthMigrationHistory.objects.filter(app_name='main').exists(): + initialize_table_definitions() + initialize_client_data() + + # Sync the DBEntities to tables in the global schema + initialize_global_config(**kwargs) + + # Send a message telling Publishers to save their default media + initialize_default_media() + initialize_presentations.send(sender=models) + create_data_provider_data(**kwargs) + + +def minimum_initialization(**kwargs): + """ + A minimum initialization for unit tests + :param kwargs: 'limit_to_classes' as an array of ConfigEntity classes to limit processing to those + :return: + """ + if SouthMigrationHistory.objects.filter(app_name='main').exists(): + # Disable built_forms + from footprint.main.publishing import built_form_publishing + post_save_config_entity_initial.disconnect( + built_form_publishing.on_config_entity_post_save_built_form, + GlobalConfig, + True, + "built_form_publishing_on_config_entity_post_save") + + initialize_table_definitions() + # Sync the DBEntities to tables in the global schema + initialize_global_config(**kwargs) + # Send a message telling Publishers to save their default media + initialize_default_media() + initialize_presentations.send(sender=models) + + # Get access to the ConfigEntity fixtures for the configured client + # For now we just try our soul client as the fixture schema, the client string is equivalent to Region schema + config_entities_fixture = resolve_fixture( + "config_entity", + "config_entities", + ConfigEntitiesFixture, + settings.CLIENT) + + # For speed, just set up the first project + project_fixture = config_entities_fixture.projects()[0] + project = DataProvider().projects(project_fixtures=[project_fixture])[0] + # For speed, just set up the first scenario + scenario_fixture = config_entities_fixture.scenarios(project=project)[0] + DataProvider().scenarios([scenario_fixture], [project_fixture]) + # Create the test user + DataProvider().user() + create_data_provider_data() + + +def initialize_client_data(): + # Initialize client-specific models + logger.info("importing client fixtures for {client}".format(client=settings.CLIENT)) + print("importing client fixtures for {client}".format(client=settings.CLIENT)) + client_init = resolve_fixture(None, "init", InitFixture, settings.CLIENT) + print("using fixture:" + str(client_init)) + client_init.populate_models() + + +def create_data_provider_data(**kwargs): + # Creating the precooked scenarios will cause everything else to be created. + DataProvider().scenarios(**kwargs) + recalculate_project_bounds() + + +def recalculate_project_bounds(): + for project in Project.objects.all(): + project.recalculate_bounds() + + +def initialize_table_definitions(): + """ + Initialize any definition tables with constant values + """ + Interest.objects.update_or_create(key=Keys.INTEREST_OWNER) + Interest.objects.update_or_create(key=Keys.INTEREST_DEPENDENT) + Interest.objects.update_or_create(key=Keys.INTEREST_FOLLOWER) + + for building_use_subcategory, building_use in Keys.BUILDING_USE_DEFINITION_CATEGORIES.items(): + BuildingUseDefinition.objects.update_or_create(name=building_use_subcategory) + BuildingUseDefinition.objects.update_or_create(name=building_use) + + for component in Keys.COMPONENT_CATEGORIES: + PlacetypeComponentCategory.objects.update_or_create( + name=component, + contributes_to_net=True if component in Keys.NET_COMPONENTS else False + ) + + # Ways to sort PresentationMedia QuerySets + SortType.objects.update_or_create( + key=Keys.SORT_TYPE_PRESENTATION_MEDIA_DB_ENTITY_KEY, + defaults={'order_by': 'db_entity_key'}) + SortType.objects.update_or_create( + key=Keys.SORT_TYPE_PRESENTATION_MEDIA_MEDIUM_KEY, + defaults={'order_by': 'medium__key'}) + SortType.objects.update_or_create( + key=Keys.SORT_TYPE_PRESENTATION_MEDIA_MEDIUM_NAME, + defaults={'order_by': 'medium__name'}) + # Ways to sort Key-ed QuerySets + SortType.objects.update_or_create( + key=Keys.SORT_TYPE_KEY, + defaults={'order_by': 'key'}) + SortType.objects.update_or_create( + key=Keys.SORT_TYPE_NAME, + defaults={'order_by': 'name'}) + + +def initialize_global_config(**kwargs): + global_bounds = MultiPolygon( + [Polygon(( + (settings.DEFAULT_SRID_BOUNDS[1], settings.DEFAULT_SRID_BOUNDS[1]), # bottom left + (settings.DEFAULT_SRID_BOUNDS[0], settings.DEFAULT_SRID_BOUNDS[3]), # top left + (settings.DEFAULT_SRID_BOUNDS[2], settings.DEFAULT_SRID_BOUNDS[3]), # top right + (settings.DEFAULT_SRID_BOUNDS[2], settings.DEFAULT_SRID_BOUNDS[1]), # bottom right + (settings.DEFAULT_SRID_BOUNDS[1], settings.DEFAULT_SRID_BOUNDS[1]), # bottom left + ))], + srid=settings.DEFAULT_SRID + ) + + # Consume default data hardcoded in the system, either from the defaults.data or tests.data package + data_provider = DataProvider() + + data_provider.user() + + limit_to_classes = kwargs.get('limit_to_classes', [GlobalConfig]) \ + if kwargs.get('limit_to_classes', [GlobalConfig]) else [GlobalConfig] + # Create and persist the singleton GlobalConfig + global_config, created, updated = GlobalConfig.objects.update_or_create( + key=Keys.GLOBAL_CONFIG_KEY, + defaults={'name': Keys.GLOBAL_CONFIG_NAME, 'bounds': global_bounds} + ) if GlobalConfig in limit_to_classes else (GlobalConfig.objects.get(), False, False) + + return global_config + + +def initialize_default_media(): + """ + Initialize default media that is used by presentations in the absence of custom values + """ + # Send a signal to publishers + initialize_media.send(sender=models) + + diff --git a/footprint/main/models/base/__init__.py b/footprint/main/models/base/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/base/base_demographic_feature.py b/footprint/main/models/base/base_demographic_feature.py new file mode 100644 index 000000000..ebbe14b79 --- /dev/null +++ b/footprint/main/models/base/base_demographic_feature.py @@ -0,0 +1,52 @@ +from footprint.main.models.geospatial.feature import Feature +from django.db import models + +__author__ = 'calthorpe_associates' + + +class BaseDemographicFeature(Feature): + + du_occupancy_rate = models.DecimalField(max_digits=14, decimal_places=4) + pop_male = models.DecimalField(max_digits=14, decimal_places=4) + pop_female = models.DecimalField(max_digits=14, decimal_places=4) + + pop_avg_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + + pop_female_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + pop_male_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + + pop_age16_up = models.DecimalField(max_digits=14, decimal_places=4) + pop_age25_up = models.DecimalField(max_digits=14, decimal_places=4) + pop_age65_up = models.DecimalField(max_digits=14, decimal_places=4) + + pop_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + pop_hs_not_comp = models.DecimalField(max_digits=14, decimal_places=4) + pop_hs_diploma = models.DecimalField(max_digits=14, decimal_places=4) + pop_some_college = models.DecimalField(max_digits=14, decimal_places=4) + pop_college_degree = models.DecimalField(max_digits=14, decimal_places=4) + pop_graduate_degree = models.DecimalField(max_digits=14, decimal_places=4) + pop_employed = models.DecimalField(max_digits=14, decimal_places=4) + + hh_inc_00_10 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_10_20 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_20_30 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_30_40 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_40_50 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_50_60 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_60_75 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_75_100 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_100_125 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_125_150 = models.DecimalField(max_digits=14, decimal_places=4) + + hh_inc_150_200 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_200p = models.DecimalField(max_digits=14, decimal_places=4) + hh_avg_vehicles = models.DecimalField(max_digits=14, decimal_places=4) + hh_avg_size = models.DecimalField(max_digits=14, decimal_places=4) + hh_agg_inc = models.DecimalField(max_digits=14, decimal_places=4) + hh_avg_inc = models.DecimalField(max_digits=14, decimal_places=4) + hh_owner_occ = models.DecimalField(max_digits=14, decimal_places=4) + hh_rental_occ = models.DecimalField(max_digits=14, decimal_places=4) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/base/base_feature.py b/footprint/main/models/base/base_feature.py new file mode 100644 index 000000000..48a4b300f --- /dev/null +++ b/footprint/main/models/base/base_feature.py @@ -0,0 +1,136 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models + +from footprint.main.models.geospatial.feature import PaintingFeature +from footprint.main.models.keys.keys import Keys + + +__author__ = 'calthorpe_associates' + + +class BaseFeature(PaintingFeature): + + built_form_key = models.CharField(max_length=250, null=True, blank=True, default=None, db_column='built_form') + region_lu_code = models.CharField(max_length=250, null=True, blank=True, default=None) + intersection_density_sqmi = models.DecimalField(max_digits=14, decimal_places=4, default=0) + intersection_count = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_gross = models.DecimalField(max_digits=14, decimal_places=4, default=0) + sqft_parcel = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_detsf = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_attsf = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_mf = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_off = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_ret = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_ind = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_ag = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_mixed = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_military = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_mixed = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_mixed_w_off = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_mixed_no_off = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_no_use = models.DecimalField(max_digits=14, decimal_places=4) + + pop = models.DecimalField(max_digits=14, decimal_places=4) + hh = models.DecimalField(max_digits=14, decimal_places=4) + + du = models.DecimalField(max_digits=14, decimal_places=4) + du_detsf = models.DecimalField(max_digits=14, decimal_places=4) + du_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4) + du_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4) + du_attsf = models.DecimalField(max_digits=14, decimal_places=4) + du_mf = models.DecimalField(max_digits=14, decimal_places=4) + du_mf2to4 = models.DecimalField(max_digits=14, decimal_places=4) + du_mf5p = models.DecimalField(max_digits=14, decimal_places=4) + + emp = models.DecimalField(max_digits=14, decimal_places=4) + + emp_ret = models.DecimalField(max_digits=14, decimal_places=4) + + emp_retail_services = models.DecimalField(max_digits=14, decimal_places=4) + emp_restaurant = models.DecimalField(max_digits=14, decimal_places=4) + emp_accommodation = models.DecimalField(max_digits=14, decimal_places=4) + emp_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4) + emp_other_services = models.DecimalField(max_digits=14, decimal_places=4) + + emp_off = models.DecimalField(max_digits=14, decimal_places=4) + + emp_office_services = models.DecimalField(max_digits=14, decimal_places=4) + emp_public_admin = models.DecimalField(max_digits=14, decimal_places=4) + emp_education = models.DecimalField(max_digits=14, decimal_places=4) + emp_medical_services = models.DecimalField(max_digits=14, decimal_places=4) + + emp_ind = models.DecimalField(max_digits=14, decimal_places=4) + + emp_manufacturing = models.DecimalField(max_digits=14, decimal_places=4) + emp_wholesale = models.DecimalField(max_digits=14, decimal_places=4) + emp_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4) + emp_utilities = models.DecimalField(max_digits=14, decimal_places=4) + emp_construction = models.DecimalField(max_digits=14, decimal_places=4) + + emp_ag = models.DecimalField(max_digits=14, decimal_places=4) + emp_agriculture = models.DecimalField(max_digits=14, decimal_places=4) + emp_extraction = models.DecimalField(max_digits=14, decimal_places=4) + + emp_military = models.DecimalField(max_digits=14, decimal_places=4) + + bldg_sqft_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_attsf = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_mf = models.DecimalField(max_digits=14, decimal_places=4) + + bldg_sqft_retail_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_restaurant = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_accommodation = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_other_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_office_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_public_admin = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_education = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_medical_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_wholesale = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4) + + residential_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4) + commercial_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4) + + class Meta(object): + abstract = True + app_label = 'main' + + def update_built_form_id(self): + old_id = self.built_form_id + + buildingtype = Keys.BUILDINGTYPE_SOURCE_ID_LOOKUP['by_source_id'].get( + self.built_form_id or None) + id = Keys.BUILDINGTYPE_SOURCE_ID_LOOKUP['by_name'].get(buildingtype or None) + + if id: + self.built_form_id = id['fp_id'] + self.save() + else: + warning = "translation-only type : built_form_id {id}".format(id=old_id) + print warning + diff --git a/footprint/main/models/base/base_parcel_feature.py b/footprint/main/models/base/base_parcel_feature.py new file mode 100644 index 000000000..01eed3c68 --- /dev/null +++ b/footprint/main/models/base/base_parcel_feature.py @@ -0,0 +1,181 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models + +from footprint.main.models import BuiltForm +from footprint.main.models.geospatial.feature import Feature +from footprint.main.models.keys.keys import Keys + + +__author__ = 'calthorpe_associates' + + +class BaseParcelFeature(Feature): + built_form = models.ForeignKey(BuiltForm, null=True, blank=True) + land_development_category = models.CharField(max_length=250, null=True, blank=True, default=None) + region_lu_code = models.CharField(max_length=250, null=True, blank=True, default=None) + landtype = models.CharField(max_length=250, null=True, blank=True, default=None) + + intersection_density_sqmi = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_gross = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + sqft_parcel = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_detsf = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_attsf = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_mf = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_off = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_ret = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_ind = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_ag = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_mixed = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_military = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_mixed = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_mixed_w_off = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_mixed_no_off = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_no_use = models.DecimalField(max_digits=14, decimal_places=4) + + du_occupancy_rate = models.DecimalField(max_digits=14, decimal_places=4) + hh = models.DecimalField(max_digits=14, decimal_places=4) + + du = models.DecimalField(max_digits=14, decimal_places=4) + du_detsf = models.DecimalField(max_digits=14, decimal_places=4) + du_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4) + du_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4) + du_attsf = models.DecimalField(max_digits=14, decimal_places=4) + du_mf = models.DecimalField(max_digits=14, decimal_places=4) + du_mf2to4 = models.DecimalField(max_digits=14, decimal_places=4) + du_mf5p = models.DecimalField(max_digits=14, decimal_places=4) + + emp = models.DecimalField(max_digits=14, decimal_places=4) + + emp_ret = models.DecimalField(max_digits=14, decimal_places=4) + + emp_retail_services = models.DecimalField(max_digits=14, decimal_places=4) + emp_restaurant = models.DecimalField(max_digits=14, decimal_places=4) + emp_accommodation = models.DecimalField(max_digits=14, decimal_places=4) + emp_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4) + emp_other_services = models.DecimalField(max_digits=14, decimal_places=4) + + emp_off = models.DecimalField(max_digits=14, decimal_places=4) + + emp_office_services = models.DecimalField(max_digits=14, decimal_places=4) + emp_public_admin = models.DecimalField(max_digits=14, decimal_places=4) + emp_education = models.DecimalField(max_digits=14, decimal_places=4) + emp_medical_services = models.DecimalField(max_digits=14, decimal_places=4) + + emp_ind = models.DecimalField(max_digits=14, decimal_places=4) + + emp_manufacturing = models.DecimalField(max_digits=14, decimal_places=4) + emp_wholesale = models.DecimalField(max_digits=14, decimal_places=4) + emp_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4) + emp_utilities = models.DecimalField(max_digits=14, decimal_places=4) + emp_construction = models.DecimalField(max_digits=14, decimal_places=4) + + emp_ag = models.DecimalField(max_digits=14, decimal_places=4) + emp_agriculture = models.DecimalField(max_digits=14, decimal_places=4) + emp_extraction = models.DecimalField(max_digits=14, decimal_places=4) + + emp_military = models.DecimalField(max_digits=14, decimal_places=4) + + bldg_sqft_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_attsf = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_mf = models.DecimalField(max_digits=14, decimal_places=4) + + bldg_sqft_retail_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_restaurant = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_accommodation = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_other_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_office_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_public_admin = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_education = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_medical_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_wholesale = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4) + + residential_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4) + commercial_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4) + + pop = models.DecimalField(max_digits=14, decimal_places=4) + pop_male = models.DecimalField(max_digits=14, decimal_places=4) + pop_female = models.DecimalField(max_digits=14, decimal_places=4) + + pop_avg_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + + pop_female_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + pop_male_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + + pop_age16_up = models.DecimalField(max_digits=14, decimal_places=4) + pop_age25_up = models.DecimalField(max_digits=14, decimal_places=4) + pop_age65_up = models.DecimalField(max_digits=14, decimal_places=4) + + pop_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + pop_hs_not_comp = models.DecimalField(max_digits=14, decimal_places=4) + pop_hs_diploma = models.DecimalField(max_digits=14, decimal_places=4) + pop_some_college = models.DecimalField(max_digits=14, decimal_places=4) + pop_college_degree = models.DecimalField(max_digits=14, decimal_places=4) + pop_graduate_degree = models.DecimalField(max_digits=14, decimal_places=4) + pop_employed = models.DecimalField(max_digits=14, decimal_places=4) + + hh_inc_00_10 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_10_20 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_20_30 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_30_40 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_40_50 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_50_60 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_60_75 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_75_100 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_100_125 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_125_150 = models.DecimalField(max_digits=14, decimal_places=4) + + hh_inc_150_200 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_200p = models.DecimalField(max_digits=14, decimal_places=4) + hh_avg_vehicles = models.DecimalField(max_digits=14, decimal_places=4) + hh_avg_size = models.DecimalField(max_digits=14, decimal_places=4) + hh_agg_inc = models.DecimalField(max_digits=14, decimal_places=4) + hh_avg_inc = models.DecimalField(max_digits=14, decimal_places=4) + hh_owner_occ = models.DecimalField(max_digits=14, decimal_places=4) + hh_rental_occ = models.DecimalField(max_digits=14, decimal_places=4) + + + class Meta(object): + abstract = True + app_label = 'main' + + def update_built_form_id(self): + old_id = self.built_form_id + + buildingtype = Keys.BUILDINGTYPE_SOURCE_ID_LOOKUP['by_source_id'].get( + self.built_form_id or None) + id = Keys.BUILDINGTYPE_SOURCE_ID_LOOKUP['by_name'].get(buildingtype or None) + + if id: + self.built_form_id = id['fp_id'] + self.save() + else: + warning = "translation-only type : built_form_id {id}".format(id=old_id) + print warning + diff --git a/footprint/main/models/base/census_block.py b/footprint/main/models/base/census_block.py new file mode 100644 index 000000000..1172bbe63 --- /dev/null +++ b/footprint/main/models/base/census_block.py @@ -0,0 +1,76 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.geospatial.feature import Feature + + +class CensusBlock(Feature): + + objects = GeoInheritanceManager() + block = models.CharField(max_length=20) + blockgroup = models.CharField(max_length=20) + tract = models.CharField(max_length=20) + du_attsf_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + du_mf2to4_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + du_mf5p_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_own_occ_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_rent_occ_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_00_10_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_10_20_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_20_30_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_30_40_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_40_50_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_50_60_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_60_75_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_75_100_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_100_125_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_125_150_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_150_200_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_inc_200p_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_agg_inc_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + hh_agg_veh_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_female_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_male_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age0_4_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age5_9_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age10_14_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age15_17_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age18_19_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age20_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age21_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age22_24_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age25_29_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age30_39_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age40_49_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age50_64_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age65_up_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age16_up_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_age25_up_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_female_age20_64_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_male_age20_64_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_hs_not_comp_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_hs_diploma_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_assoc_some_coll_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_coll_degree_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_grad_degree_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_employed_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + pop_unemployed_rate = models.DecimalField(max_digits=14, decimal_places=6, default=0) + + @classmethod + def dynamic_fields(cls): + return dict(census_blockgroup=dict(field_class_name='django.db.models.ForeignKey')) + + class Meta: + abstract = True + app_label = 'main' diff --git a/footprint/main/models/base/census_blockgroup.py b/footprint/main/models/base/census_blockgroup.py new file mode 100644 index 000000000..fc0c0dedb --- /dev/null +++ b/footprint/main/models/base/census_blockgroup.py @@ -0,0 +1,35 @@ +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models + +class CensusBlockgroup(Feature): + objects = GeoInheritanceManager() + blockgroup = models.CharField(max_length=20) + + # Used to create census_track ForeignKey + tract = models.CharField(max_length=20) + + @classmethod + def related_fields(cls): + return dict(built_forms=dict( + single=True, + related_class_name='footprint.main.models.built_form', + source_field='key', + source_class_join_field_name='built_form')) + + class Meta: + abstract = True + app_label = 'main' diff --git a/footprint/main/models/base/census_tract.py b/footprint/main/models/base/census_tract.py new file mode 100644 index 000000000..30416ef46 --- /dev/null +++ b/footprint/main/models/base/census_tract.py @@ -0,0 +1,26 @@ +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models + +class CensusTract(Feature): + + objects = GeoInheritanceManager() + tract = models.CharField(max_length=20) + + class Meta: + abstract = True + app_label = 'main' diff --git a/footprint/main/models/base/cpad_holdings_feature.py b/footprint/main/models/base/cpad_holdings_feature.py new file mode 100644 index 000000000..b4e21444e --- /dev/null +++ b/footprint/main/models/base/cpad_holdings_feature.py @@ -0,0 +1,65 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class CpadHoldingsFeature(Feature): + agency_name = models.CharField(max_length=100, null=True, blank=True) + unit_name = models.CharField(max_length=100, null=True, blank=True) + access_type = models.CharField(max_length=100, null=True, blank=True) + acres = models.DecimalField(max_digits=10, decimal_places=2) + county = models.CharField(max_length=100, null=True, blank=True) + agency_level = models.CharField(max_length=100, null=True, blank=True) + agency_website = models.CharField(max_length=300, null=True, blank=True) + site_website = models.CharField(max_length=300, null=True, blank=True) + layer = models.CharField(max_length=100, null=True, blank=True) + management_agency = models.CharField(max_length=100, null=True, blank=True) + label_name = models.CharField(max_length=100, null=True, blank=True) + ownership_type = models.CharField(max_length=100, null=True, blank=True) + site_name = models.CharField(max_length=100, null=True, blank=True) + alternate_site_name = models.CharField(max_length=100, null=True, blank=True) + land_water = models.CharField(max_length=100, null=True, blank=True) + specific_use = models.CharField(max_length=100, null=True, blank=True) + hold_notes = models.CharField(max_length=320, null=True, blank=True) + city = models.CharField(max_length=100, null=True, blank=True) + + designation_agency = models.CharField(max_length=100, null=True, blank=True) + designation = models.CharField(max_length=100, null=True, blank=True) + primary_purpose = models.CharField(max_length=100, null=True, blank=True) + apn = models.CharField(max_length=100, null=True, blank=True) + holding_id = models.CharField(max_length=100, null=True, blank=True) + unit_id = models.CharField(max_length=100, null=True, blank=True) + + superunit = models.CharField(max_length=100, null=True, blank=True) + agency_id = models.CharField(max_length=100, null=True, blank=True) + mng_ag_id = models.CharField(max_length=100, null=True, blank=True) + al_av_parc = models.CharField(max_length=100, null=True, blank=True) + date_revised = models.CharField(max_length=100, null=True, blank=True) + src_align = models.CharField(max_length=100, null=True, blank=True) + src_attr = models.CharField(max_length=100, null=True, blank=True) + d_acq_yr = models.CharField(max_length=100, null=True, blank=True) + + + class Meta(object): + abstract = True + app_label = 'main' + diff --git a/footprint/main/models/base/developable_feature.py b/footprint/main/models/base/developable_feature.py new file mode 100644 index 000000000..e4aa42189 --- /dev/null +++ b/footprint/main/models/base/developable_feature.py @@ -0,0 +1,111 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + +class DevelopableFeature(Feature): + developable_index = models.DecimalField(max_digits=14, decimal_places=6, default=0) + acres_gross = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_developable = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_urban = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_greenfield = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_constrained = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res_detsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res_attsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res_mf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_ret = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_off = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_ind = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_ag = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_mixed = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_military = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_mixed = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_mixed_w_off = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_mixed_no_off = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_no_use = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + pop = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_detsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_attsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_mf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_mf2to4 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_mf5p = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_ret = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_retail_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_restaurant = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_accommodation = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_other_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_off = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_office_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_education = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_public_admin = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_medical_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp_ind = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_wholesale = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_manufacturing = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_utilities = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_construction = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_ag = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_agriculture = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_extraction = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_military = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + bldg_sqft_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_attsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_mf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_retail_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_restaurant = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_accommodation = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_other_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_office_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_public_admin = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_medical_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_education = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_wholesale = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + commercial_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4, default=0) + residential_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + + def __unicode__(self): + return unicode("Dev Acres config for %s" % self.scenario.name) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/base/primary_parcel_feature.py b/footprint/main/models/base/primary_parcel_feature.py new file mode 100644 index 000000000..1cfa9a295 --- /dev/null +++ b/footprint/main/models/base/primary_parcel_feature.py @@ -0,0 +1,15 @@ +from footprint.main.models.geospatial.feature import Feature + +__author__ = 'calthorpe_associates' + + +class PrimaryParcelFeature(Feature): + # Every subclass must define a land_use_definition ForeignKey whose type is a subclass of ClientLandUseDefinition + # land_use_definition = models.ForeignKey(ClientLandUseDefinition, null=True) + + # Dynamically created by dynamic subclasses + # census_block = models.ForeignKey(CensusBlock, null=True) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/built_form/__init__.py b/footprint/main/models/built_form/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/built_form/building_attribute_set.py b/footprint/main/models/built_form/building_attribute_set.py new file mode 100644 index 000000000..32fff4ebe --- /dev/null +++ b/footprint/main/models/built_form/building_attribute_set.py @@ -0,0 +1,191 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +from decimal import Decimal +from django.db.models import Sum +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.constants import Constants +from footprint.main.models.built_form.building_use_percent import BuildingUsePercent +from footprint.main.utils.utils import get_or_none_from_queryset + +__author__ = 'calthorpe_associates' +from collections import defaultdict +from django.contrib.gis.db import models +from footprint.main.models.built_form.building_use_definition import BuildingUseDefinition +from footprint.main.models.keys.keys import Keys + + +class BuildingAttributeSet(models.Model): + """ + Attributes of a :models:`main.Building`, :models:`main.BuildingType`, or :models:`main.Placetype`, + including a reference to its uses through :model:`built_form.building_use_percent.BuildingUsePercent`. + """ + objects = GeoInheritanceManager() + + class Meta(object): + abstract = False + app_label = 'main' + + def attributes(self): + return "building" + + ## fields applicable at the Building level and above : + building_uses = models.ManyToManyField(BuildingUseDefinition, through="BuildingUsePercent") + + parking_spaces = models.DecimalField(max_digits=7, decimal_places=3, default=0) + parking_structure_square_feet = models.DecimalField(max_digits=9, decimal_places=2, default=0) + + floors = models.DecimalField(max_digits=7, decimal_places=3, null=True) + total_far = models.DecimalField(max_digits=10, decimal_places=7, null=True) + + # population fields + gross_population_density = models.DecimalField(max_digits=14, decimal_places=10, default=0) + household_density = models.DecimalField(max_digits=14, decimal_places=10, default=0) + + # these should really be optional / derived ... + impervious_roof_percent = models.DecimalField(max_digits=5, decimal_places=3, null=True) + impervious_hardscape_percent = models.DecimalField(max_digits=5, decimal_places=3, null=True) + pervious_hardscape_percent = models.DecimalField(max_digits=5, decimal_places=3, null=True) + softscape_and_landscape_percent = models.DecimalField(max_digits=5, decimal_places=3, null=True) + + irrigated_percent = models.DecimalField(max_digits=5, decimal_places=3, null=True) + hardscape_percent = models.DecimalField(max_digits=5, decimal_places=3, null=True) + residential_irrigated_square_feet = models.DecimalField(max_digits=9, decimal_places=2, null=True) + commercial_irrigated_square_feet = models.DecimalField(max_digits=9, decimal_places=2, null=True) + + residential_average_lot_size = models.DecimalField(max_digits=9, decimal_places=2, null=True) + + gross_net_ratio = models.DecimalField(max_digits=8, decimal_places=7, default=1) + + combined_pop_emp_density = models.DecimalField(max_digits=9, decimal_places=4, default=0) + + # methods to get at derived facts about building attributes + def calculate_combined_pop_emp_density(self): + employment_uses = self.buildingusepercent_set.filter( + building_use_definition__name__in=Keys.TOP_LEVEL_EMPLOYMENT_CATEGORIES) + + emp_density = self.get_unit_density(employment_uses) if employment_uses.exists else 0 + self.combined_pop_emp_density = float(emp_density) + float(self.gross_population_density or 0) + + def calculate_derived_fields(self): + """ + takes the basic input data about the building attributes of a built form, and calculates useful derived fields + for use by the flat_built_forms exporter and eventually in integrated UF analyses + :return: + """ + from footprint.main.models.built_form.primary_component import PrimaryComponent + + for use_percent in self.buildingusepercent_set.all(): + if isinstance(self.builtform_set.all().select_subclasses()[0], PrimaryComponent): + use_percent.calculate_derived_attributes() + + self.calculate_use_metacategories() + + self.calculate_residential_attributes() + + # derive irrigated square feet fields + self.hardscape_percent = (self.impervious_roof_percent or 0) + (self.impervious_hardscape_percent or 0) + + if self.floors and not self.hardscape_percent: + building_footprint = self.total_far / self.floors + parking_space_area = self.parking_spaces * Constants.PARKING_STALL_SQUARE_FEET + uncovered_parking_area = parking_space_area - self.parking_structure_square_feet + self.hardscape_percent = (building_footprint + uncovered_parking_area) / Constants.SQUARE_FEET_PER_ACRE + + commercial_uses = self.buildingusepercent_set.filter( + building_use_definition__name__in=['Retail', 'Office', 'Industrial'] + ) + residential_uses = self.buildingusepercent_set.filter( + building_use_definition__name__in=Keys.RESIDENTIAL_SUBCATEGORIES + ) + + percent_residential = residential_uses.aggregate(Sum('percent'))['percent__sum'] or 0 + percent_commercial = commercial_uses.aggregate(Sum('percent'))['percent__sum'] or 0 + + available_irrigation_area = (self.softscape_and_landscape_percent or 0) * Constants.SQUARE_FEET_PER_ACRE + irrigated_area = available_irrigation_area * (self.irrigated_percent or 0) + + self.residential_irrigated_square_feet = float(percent_residential) * float(irrigated_area or 0) + self.commercial_irrigated_square_feet = float(percent_commercial) * float(irrigated_area or 0) + self.calculate_combined_pop_emp_density() + self.save() + + def get_unit_density(self, use_percent_set): + return sum(building_use.unit_density for building_use in use_percent_set) + + def calculate_use_metacategories(self): + """ + Calculates upper level uses for the built form based on the attributes + :return: + """ + metacategories = ["RESIDENTIAL", "OFFICE", "RETAIL", "INDUSTRIAL", "AGRICULTURAL"] + + for metacategory in metacategories: + + metacategory_name = getattr(Keys, "{0}_CATEGORY".format(metacategory)) + metacategory_definition = BuildingUseDefinition.objects.get_or_create(name=metacategory_name)[0] + subcategory_names = getattr(Keys, "{0}_SUBCATEGORIES".format(metacategory)) + + subcategories = self.buildingusepercent_set.filter(building_use_definition__name__in=subcategory_names) + + metacategory_dict = defaultdict(lambda: Decimal(0.000000000000)) + + metacategory_dict['percent'] = subcategories.aggregate(Sum('percent'))['percent__sum'] + if not metacategory_dict['percent']: + continue + + averaged_attributes = metacategory_definition.averaged_attributes() + summed_attributes = metacategory_definition.summed_attributes() + + for attribute in averaged_attributes: + metacategory_dict[attribute] = sum([ + getattr(subcategory, attribute) * (subcategory.percent / metacategory_dict['percent']) + for subcategory in subcategories + ]) + for attribute in summed_attributes: + metacategory_dict[attribute] = sum([getattr(subcategory, attribute) for subcategory in subcategories]) + + + BuildingUsePercent.objects.update_or_create( + building_attributes=self, + building_use_definition=BuildingUseDefinition.objects.get(name=metacategory_name), + defaults=metacategory_dict + ) + + return BuildingUsePercent.objects.filter( + building_use_definition__name__in=Keys.BUILDING_USE_DEFINITION_METACATEGORIES + ) + + def calculate_residential_attributes(self): + """ + does special calculations for residential attributes, or just returns if there isn't any residential use: + + household_density = [unit_density] * (1 - vacancy_rate) + + gross_population_density = [household_density] * [household_size] + + :return: + """ + + use = get_or_none_from_queryset( + self.buildingusepercent_set, + building_use_definition__name=Keys.RESIDENTIAL_CATEGORY + ) + + if not use: + return + + self.household_density = use.unit_density * (1-use.vacancy_rate) + self.gross_population_density = self.household_density * use.household_size + self.save() \ No newline at end of file diff --git a/footprint/main/models/built_form/building_use_definition.py b/footprint/main/models/built_form/building_use_definition.py new file mode 100644 index 000000000..006f42910 --- /dev/null +++ b/footprint/main/models/built_form/building_use_definition.py @@ -0,0 +1,34 @@ +# coding=utf-8 +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager + +__author__ = 'calthorpe_associates' + +from footprint.main.mixins.name import Name +from django.db import models +from footprint.main.models.keys.keys import Keys + +class BuildingUseDefinition(Name): + """ + BuildingUseDefinition describes the possible general types of uses for a building + """ + objects = GeoInheritanceManager() + + category = models.ForeignKey('BuildingUseDefinition', null=True) + + class Meta(object): + app_label = 'main' + + def get_attributes(self): + return self.averaged_attributes() + self.summed_attributes() + + def averaged_attributes(self): + return ['efficiency', 'square_feet_per_unit', 'vacancy_rate', 'floor_area_ratio'] + \ + (['household_size'] if self.name in Keys.ALL_RESIDENTIAL_USES else []) + + def summed_attributes(self): + return ['unit_density', 'net_built_up_area', 'gross_built_up_area'] + + def clean(self): + use_category_dict = Keys.BUILDING_USE_DEFINITION_CATEGORIES + category = BuildingUseDefinition.objects.get(Name=use_category_dict[self.name]) or None + self.category = category or 'Unknown' diff --git a/footprint/main/models/built_form/building_use_percent.py b/footprint/main/models/built_form/building_use_percent.py new file mode 100644 index 000000000..07ec7b32f --- /dev/null +++ b/footprint/main/models/built_form/building_use_percent.py @@ -0,0 +1,82 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models + +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.percent import Percent +from footprint.main.models.constants import Constants +from footprint.main.models.keys.keys import Keys + + +__author__ = 'calthorpe_associates' + + +class BuildingUsePercent(Percent): + """ + Describes the percent of :model:`main.BuildingUseDefinition` present in a particular + :model:`main.BuildingAttributeSet`. + """ + objects = GeoInheritanceManager() + + building_attributes = models.ForeignKey('BuildingAttributeSet') + building_use_definition = models.ForeignKey('BuildingUseDefinition') + + vacancy_rate = models.DecimalField(max_digits=4, decimal_places=3, default=Constants.VACANCY_RATE) + household_size = models.DecimalField(max_digits=5, decimal_places=3, null=True) + + # describes the ratio : use areas / (common areas + use areas) + efficiency = models.DecimalField(max_digits=6, decimal_places=4, default=.85) + + # describes the number of square feet per unit ( dwelling unit or employee ) of the building + square_feet_per_unit = models.DecimalField(max_digits=11, decimal_places=3, null=True) + + ## derived attributes + # area measured in acres + floor_area_ratio = models.DecimalField(max_digits=12, decimal_places=10, null=True) + unit_density = models.DecimalField(max_digits=16, decimal_places=10, null=True) + + # area measured in square feet + gross_built_up_area = models.DecimalField(max_digits=13, decimal_places=3, null=True) + net_built_up_area = models.DecimalField(max_digits=13, decimal_places=3, null=True) + + class Meta(object): + app_label = 'main' + + def calculate_derived_attributes(self): + """ + Calculates floor_area_ratio, gross_built_up_area, net_built_up_area, and use_density based on the + inputs self.built_form.total_far, self.built_form.gross_net_ratio, self.percent, self.efficiency, and + either self.square_feet_per_unit or self.built_form.residential_average_lot_size (the latter for detached + residential building_use_definitions). + """ + + if not self.floor_area_ratio: + parcel_far = self.building_attributes.total_far # / self.building_attributes.gross_net_ratio + self.floor_area_ratio = self.percent * parcel_far + self.gross_built_up_area = self.floor_area_ratio * Constants.SQUARE_FEET_PER_ACRE + self.net_built_up_area = self.gross_built_up_area * self.efficiency + percent_of_parcel_acres = self.percent # / self.building_attributes.gross_net_ratio + + if self.building_use_definition.name in Keys.DETACHED_RESIDENTIAL and self.building_attributes.residential_average_lot_size: + residential_lots_per_acre = Constants.SQUARE_FEET_PER_ACRE / self.building_attributes.residential_average_lot_size + self.unit_density = percent_of_parcel_acres * residential_lots_per_acre + else: + self.unit_density = self.net_built_up_area / self.square_feet_per_unit + + self.save() \ No newline at end of file diff --git a/footprint/main/models/built_form/built_form.py b/footprint/main/models/built_form/built_form.py new file mode 100644 index 000000000..2cff9fdfe --- /dev/null +++ b/footprint/main/models/built_form/built_form.py @@ -0,0 +1,265 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import glob +import os +import subprocess + +from django.db import models +from django.template.defaultfilters import slugify +from model_utils.managers import InheritanceManager +from django.conf import settings + +from footprint.main.mixins.building_attributes import BuildingAttributes +from footprint.main.mixins.deletable import Deletable +from footprint.main.mixins.key import Key +from footprint.main.mixins.name import Name +from footprint.main.mixins.tags import Tags +from footprint.main.models.presentation.medium import Medium +from footprint.main.models.presentation.built_form_example import BuiltFormExample +from footprint.main.utils.utils import timestamp +from footprint.main.utils.utils import get_or_none + + +__author__ = 'calthorpe_associates' + + +class BuiltForm(Name, Key, Tags, BuildingAttributes, Deletable): + """ + The base type for :model:`auth.User`, :model:`main.built_form.building.Building`, Buildingtypes, Placetypes, Infrastructure and InfrastructureTypes + + """ + objects = InheritanceManager() + origin_built_form = models.ForeignKey('BuiltForm', null=True) + medium = models.ForeignKey('Medium', null=True) + media = models.ManyToManyField(Medium, related_name='built_form_media') + examples = models.ManyToManyField(BuiltFormExample, null=True) + + BUILT_FORM_IMAGES_DIR = 'builtform_images' + + class Meta(object): + # This is not abstract so that django can form a many-to-many relationship with it in built_form_set + abstract = False + app_label = 'main' + + # Returns the string representation of the model. + def __unicode__(self): + return self.name + + # def register_component_changes(self): + # + # component_field = self.get_component_field() + # if component_field: + # m2m_changed.connect(on_collection_modify, sender=component_field.through, weak=False) + + def get_building_attribute_class(self): + """ + Looks up the child class (Placetype, BuildingType, Building) + + :return: Child class of the built form or None if the BuiltForm is has no Building components + """ + # this is rough, but it's a way to look up which model the built form is living in (we're only interested in + # BuildingTypes and PlaceTypes for this release + from footprint.main.models import Placetype, PlacetypeComponent, PrimaryComponent + + for clazz in PrimaryComponent, PlacetypeComponent, Placetype: + built_form = get_or_none(clazz, id=self.id) + if built_form: + return built_form + return None + + def get_component_field(self): + """ + Returns the ModelField that holds this class's component instances (e.g. placetype_components for the + PlaceType class) + + :return: + """ + return None + + def get_components(self, **kwargs): + """ + Returns the component instances of this class, optionally filtered with the given kwargs. Returns None for + classes without components (e.g. Buildings) + :param kwargs: Typical Query parameters used to filter the results + + :return: + """ + component_field = self.get_component_field() + if not component_field: + return None + + return component_field.filter(**kwargs) if len(kwargs) > 0 else component_field.all() + + def custom_aggregation_methods(self): + pass + + def aggregate_built_form_attributes(self): + """ + Sums the basic facts of built forms up to their aggregate type. + :return: None + """ + pass + + def create_built_form_medium(self, color): + medium_key = "built_form_{0}".format(self.id) + medium = update_or_create_built_form_medium(medium_key, color) + self.medium = medium + self.save() + return medium + + def update_or_create_built_form_media(self): + + built_form_key = self.key + path_root = '%s/sproutcore/apps/fp/resources/%s' % (settings.PROJECT_ROOT, self.BUILT_FORM_IMAGES_DIR) + + pt_specific_image_folder = '%s/%s' % (path_root, built_form_key) + default_images_folder = '%s/%s' % (path_root, 'default') + + # Create the BuiltForm's Medium if needed + # TODO we might want to use a Template here instead. We already create BuiltForm Templates for Feature classes that + # have BuiltForm styling + self.media.clear() + + image_paths = glob.glob(pt_specific_image_folder + '/*.*') + image_directory = built_form_key + + if not image_paths: + image_paths = glob.glob(default_images_folder + '/*.*') + image_directory = "default" + + # image_paths = glob.glob(pt_specific_image_folder + '/*.*') if glob.glob(pt_specific_image_folder + '/*.*') else \ + # glob.glob(default_images_folder + '/*.*') + + # if there are no images in the folder for a particular placetype, use the images in 'default' + for image_path in image_paths: + + image_name = os.path.basename(image_path) + image_key = built_form_key + '_' + image_name + media = Medium.objects.update_or_create( + key=image_key, + defaults=dict(url='%s/%s/%s' % (self.BUILT_FORM_IMAGES_DIR, image_directory, image_name)) + )[0] + self.media.add(media) + + + + def update_or_create_built_form_examples(self, array_of_examples): + + built_form_key = self.key + + self.examples.clear() + + for object in array_of_examples: + + name = object["study_area_name"] + name_slug = slugify(name).replace('-','_') + study_area_aerial_map_view = object["study_area_aerial_map_view"] + study_area_street_view = object["study_area_street_view"] + example = BuiltFormExample.objects.update_or_create( + key="%s_%s" % (built_form_key, name_slug), + defaults=dict( + url_aerial = study_area_aerial_map_view, + url_street = study_area_street_view, + name=name + ))[0] + self.examples.add(example) + + +def update_or_create_built_form_medium(medium_key, color): + # Create the BuiltForm's Medium if needed + # TODO we might want to use a Template here instead. We already create BuiltForm Templates for Feature classes that + # have BuiltForm styling + medium, created, updated = Medium.objects.update_or_create( + key=medium_key, + defaults={ + 'content_type': 'color', + 'content': {'fill': {'color': color}, } + }) + # Set color in case we're doing an update + medium.content['fill']['color'] = color + medium.save() + return medium + + +def dump_built_forms(): + """ + Exports all of the content of all of the tables that represent BuiltForm to a SQL file that can be most easily + distributed to other machines and loaded more quickly for tests. Avoids redundantly running the costly calculation + of the built form relationships. + + :return: + """ + built_form_tables = [ + "building", "buildingattributes", "buildingpercent", "buildingtype", "buildingtypecategory", + "buildingusedefinition", "buildingusepercent", "builtform", "builform_tags", + "builtformset", "builtformset_built_forms", "flatbuiltform", "infrastructure", "infrastructureattributeset", + "infrastructurepercent", "infrastructuretype", "placetype", "placetypecomponent", "placetypecomponentpercent", + "medium" + ] + + formatted_list_of_tables = "" + for table_name in built_form_tables: + formatted_list_of_tables += '-t footprint_{table_name} '.format(table_name=table_name) + + dump_args = dict( + database=settings.DATABASES['default']['NAME'], + formatted_list_of_tables=formatted_list_of_tables, + output_file="{sql_path}/built_form_export_{timestamp}.sql".format( + sql_path=settings.SQL_PATH, timestamp=timestamp() + ), + tmp_db_name="urbanfootprint_builtform_dump" + ) + + print dump_args + + dump_command = "pg_dump {database} {formatted_list_of_tables} > {output_file}" + + create_tmp_db = "createdb {tmp_db_name} --template template_postgis" + + restore_dump_to_tmp_db = "psql {tmp_db_name} -f {output_file}" + + delete_unrelated_media = """psql {tmp_db_name} -c "DELETE from footprint_medium + where \"key\"::text NOT LIKE 'built_form%'" + """ + + delete_dumpfile = "rm {output_file}" + + dump_isolated_built_form_relations = "pg_dump {tmp_db_name} {formatted_list_of_tables} > {output_file}" + + drop_tmp_db = "psql {database} -c \"drop database {tmp_db_name}\"" + + for command in [dump_command, create_tmp_db, restore_dump_to_tmp_db, delete_unrelated_media, + delete_dumpfile, dump_isolated_built_form_relations, drop_tmp_db]: + try: + subprocess.call(command.format(**dump_args), shell=True) + except Exception, E: + print E + + +def resolve_attributes_for_components(sender, **kwargs): + built_form = kwargs['instance'] + try: + built_form = built_form.aggregate() + except Exception, E: + print Exception, E + built_form.aggregate_built_form_attributes() + + +def on_collection_modify(sender, **kwargs): + resolve_attributes_for_components(sender, **kwargs) \ No newline at end of file diff --git a/footprint/main/models/built_form/built_form_set.py b/footprint/main/models/built_form/built_form_set.py new file mode 100644 index 000000000..8e6ede8eb --- /dev/null +++ b/footprint/main/models/built_form/built_form_set.py @@ -0,0 +1,35 @@ +# coding=utf-8 # UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.deletable import Deletable +from footprint.main.mixins.key import Key +from footprint.main.mixins.name import Name +from footprint.main.models.built_form.built_form import BuiltForm +from django.db import models + +__author__ = 'calthorpe_associates' + +class BuiltFormSet(Key, Name, Deletable): + """ + A BuiltFormSet is a combination of any classes inheriting BuiltForm + """ + objects = GeoInheritanceManager() + built_forms = models.ManyToManyField(BuiltForm) + + class Meta(object): + app_label = 'main' diff --git a/footprint/main/models/built_form/client_land_use.py b/footprint/main/models/built_form/client_land_use.py new file mode 100644 index 000000000..17b75e7eb --- /dev/null +++ b/footprint/main/models/built_form/client_land_use.py @@ -0,0 +1,14 @@ +from model_utils.managers import InheritanceManager +from footprint.main.models import BuiltForm + +__author__ = 'calthorpe_associates' + + +class ClientLandUse(BuiltForm): + """ + A generic class describing land use for clients to subclass. This is used by the API to deliver client-specific BuiltForm classes + """ + objects = InheritanceManager() + class Meta(object): + abstract = True + app_label = 'main' \ No newline at end of file diff --git a/footprint/main/models/built_form/client_land_use_definition.py b/footprint/main/models/built_form/client_land_use_definition.py new file mode 100644 index 000000000..8f28ed5d6 --- /dev/null +++ b/footprint/main/models/built_form/client_land_use_definition.py @@ -0,0 +1,14 @@ +from django.contrib.gis.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager + +__author__ = 'calthorpe_associates' + + +class ClientLandUseDefinition(models.Model): + """ + A generic land use definition class for clients to subclass + """ + objects = GeoInheritanceManager() + class Meta(object): + abstract = True + app_label = 'main' \ No newline at end of file diff --git a/footprint/main/models/built_form/deprecated/__init__.py b/footprint/main/models/built_form/deprecated/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/built_form/deprecated/building.py b/footprint/main/models/built_form/deprecated/building.py new file mode 100644 index 000000000..a2dae6d00 --- /dev/null +++ b/footprint/main/models/built_form/deprecated/building.py @@ -0,0 +1,48 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db.models.signals import post_save +from django.db.models import Sum +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager + + +from footprint.main.models.built_form.built_form import BuiltForm + + +class Building(BuiltForm, BuildingComponent): + """ + Building represents a template building, such as a Rural Community College + """ + objects = GeoInheritanceManager() + + class Meta(object): + # This is not abstract so that django can form a many-to-many relationship with it in built_form_set + app_label = 'main' + + def get_parent_field(self): + return self.buildingtype_set + +def on_instance_modify(sender, **kwargs): + instance = kwargs['instance'] + for parent_object in instance.get_parent_field().all(): + if parent_object.buildingpercent_set.all().aggregate(Sum('percent')) > .95: + parent_object.aggregate_built_form_attributes() + else: + pass + +post_save.connect(on_instance_modify, sender=Building) diff --git a/footprint/main/models/built_form/deprecated/building_percent.py b/footprint/main/models/built_form/deprecated/building_percent.py new file mode 100644 index 000000000..95eab4489 --- /dev/null +++ b/footprint/main/models/built_form/deprecated/building_percent.py @@ -0,0 +1,44 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager + +from footprint.main.models.built_form.building import Building +from footprint.main.models.built_form.buildingtype import BuildingType +from footprint.main.mixins.percent import Percent +from django.db import models + +__author__ = 'calthorpe_associates' + +class BuildingPercent(Percent): + """ + Many-to-many "through" class adds a percent field + """ + objects = GeoInheritanceManager() + building = models.ForeignKey(Building) + buildingtype = models.ForeignKey(BuildingType) + + class Meta(object): + app_label = 'main' + + def component(self): + return self.building + + def aggregate(self): + return self.buildingtype + diff --git a/footprint/main/models/built_form/deprecated/buildingtype.py b/footprint/main/models/built_form/deprecated/buildingtype.py new file mode 100644 index 000000000..045c8a388 --- /dev/null +++ b/footprint/main/models/built_form/deprecated/buildingtype.py @@ -0,0 +1,63 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from django.db.models.signals import post_save +#from calthorpe.main.utils.subclasses import receiver_subclasses +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.built_form.building_component import BuildingComponent +from footprint.main.mixins.building_aggregate import BuildingAttributeAggregate +from footprint.main.mixins.building_attributes import BuildingAttributes + +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from footprint.main.models.built_form.building import Building +from footprint.main.models.built_form.buildingtype_category import BuildingtypeCategory + +__author__ = 'calthorpe_associates' + +#@receiver_subclasses(post_save, ConfigEntity, "config_entity_post_save") +from built_form import on_collection_modify + +class BuildingType(BuildingComponent, BuildingAttributeAggregate, PlacetypeComponent, BuildingAttributes): + """ + BuildingType represents a mix of template building, such as a Rural Community College + """ + objects = GeoInheritanceManager() + + buildings = models.ManyToManyField(Building, through='BuildingPercent') + category = models.ForeignKey(BuildingtypeCategory, null=True) + + def __init__(self, *args, **kwargs): + super(BuildingType, self).__init__(*args, **kwargs) + + def get_component_field(self): + return self.__class__.buildings + + class Meta(object): + app_label = 'main' + + def get_parent_field(self): + return self.placetype_set + +def on_instance_modify(sender, **kwargs): + instance = kwargs['instance'] + for parent_object in instance.get_parent_field().all(): + parent_object.aggregate_built_form_attributes() + +post_save.connect(on_collection_modify, sender=BuildingType.buildings.through) +post_save.connect(on_instance_modify, sender=BuildingType) diff --git a/footprint/main/models/built_form/deprecated/buildingtype_category.py b/footprint/main/models/built_form/deprecated/buildingtype_category.py new file mode 100644 index 000000000..07c1f9fff --- /dev/null +++ b/footprint/main/models/built_form/deprecated/buildingtype_category.py @@ -0,0 +1,15 @@ +# coding=utf-8 +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.name import Name + +__author__ = 'calthorpe_associates' + +class BuildingtypeCategory(Name): + """ + BuildingUseDefinition describes the possible general types of uses for a building + """ + objects = GeoInheritanceManager() + + + class Meta(object): + app_label = 'main' diff --git a/footprint/main/models/built_form/deprecated/infrastructure.py b/footprint/main/models/built_form/deprecated/infrastructure.py new file mode 100644 index 000000000..d3cc7949b --- /dev/null +++ b/footprint/main/models/built_form/deprecated/infrastructure.py @@ -0,0 +1,18 @@ +# coding=utf-8 + + +__author__ = 'calthorpe_associates' + +from footprint.main.models.built_form.built_form import BuiltForm +from footprint.main.models.built_form.infrastructure_attributes import InfrastructureAttributeSet +class Infrastructure(InfrastructureAttributeSet, BuiltForm): + """ + Infrastructure is the container for streets, parks, detention/utilities + """ + + class Meta(object): + app_label = 'main' + + # Returns the string representation of the model. + def __unicode__(self): + return self.name diff --git a/footprint/main/models/built_form/deprecated/infrastructure_percent.py b/footprint/main/models/built_form/deprecated/infrastructure_percent.py new file mode 100644 index 000000000..af754e25a --- /dev/null +++ b/footprint/main/models/built_form/deprecated/infrastructure_percent.py @@ -0,0 +1,35 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.built_form.infrastructure import Infrastructure +from footprint.main.models.built_form.infrastructure_type import InfrastructureType +from footprint.main.mixins.percent import Percent +from django.db import models + +__author__ = 'calthorpe_associates' + +class InfrastructurePercent(Percent): + """ + Many-to-many "through" class adds a percent field + """ + infrastructure = models.ForeignKey(Infrastructure) + infrastructure_type = models.ForeignKey(InfrastructureType) + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/built_form/deprecated/infrastructure_type.py b/footprint/main/models/built_form/deprecated/infrastructure_type.py new file mode 100644 index 000000000..e98be5d49 --- /dev/null +++ b/footprint/main/models/built_form/deprecated/infrastructure_type.py @@ -0,0 +1,25 @@ +# coding=utf-8 +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from footprint.main.models.built_form.infrastructure_attributes import InfrastructureAttributeSet +from footprint.main.models.built_form.infrastructure import Infrastructure +from django.db import models +__author__ = 'calthorpe_associates' + +class InfrastructureType(PlacetypeComponent, InfrastructureAttributeSet): + """ + Infrastructure is the container for streets, parks, detention/utilities + """ + objects = GeoInheritanceManager() + + infrastructures = models.ManyToManyField(Infrastructure, through='InfrastructurePercent') + + def get_component_field(self): + return self.infrastructures + + class Meta(object): + app_label = 'main' + + # Returns the string representation of the model. + def __unicode__(self): + return self.name diff --git a/footprint/main/models/built_form/flat_built_forms.py b/footprint/main/models/built_form/flat_built_forms.py new file mode 100644 index 000000000..778be0812 --- /dev/null +++ b/footprint/main/models/built_form/flat_built_forms.py @@ -0,0 +1,495 @@ +# coding=utf-8 +import csv +import math +import logging +from collections import defaultdict +from django.db.models import Sum +from django.template.defaultfilters import slugify +from footprint.main.models.built_form.built_form import BuiltForm +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from footprint.main.models.built_form.primary_component import PrimaryComponent +from footprint.main.models.built_form.placetype import Placetype +from footprint.main.models.keys.keys import Keys + +from django.conf import settings + +__author__ = 'calthorpe_associates' + +from django.db import models +logger = logging.getLogger(__name__) + + +class FlatBuiltForm(models.Model): + built_form_id = models.IntegerField(null=False, primary_key=True) + key = models.CharField(max_length=120) + + intersection_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + name = models.CharField(max_length=100) + built_form_type = models.CharField(max_length=50) + gross_net_ratio = models.DecimalField(max_digits=11, decimal_places=10, default=0) + + # top level category densities + dwelling_unit_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + household_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + population_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + employment_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + # subcategory densities + single_family_large_lot_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + single_family_small_lot_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + attached_single_family_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + multifamily_2_to_4_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + multifamily_5_plus_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + retail_services_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + restaurant_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + arts_entertainment_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + accommodation_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + other_services_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + office_services_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + public_admin_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + education_services_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + medical_services_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + manufacturing_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + wholesale_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + transport_warehouse_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + construction_utilities_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + agriculture_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + extraction_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + armed_forces_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + office_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + retail_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + industrial_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + residential_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + agricultural_density = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + # acres parcel fields: these could be modeled more nicely, but this is fine for now + acres_parcel_mixed_use = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_residential = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_employment = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + acres_parcel_mixed_use_with_office = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_mixed_use_without_office = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + acres_parcel_residential_single_family_small_lot = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_residential_single_family_large_lot = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_residential_attached_single_family = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_residential_multifamily = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + acres_parcel_employment_office = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_employment_retail = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_employment_industrial = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_employment_agriculture = models.DecimalField(max_digits=15, decimal_places=10, default=0) + acres_parcel_employment_mixed = models.DecimalField(max_digits=15, decimal_places=10, default=0) + + # building square feet fields + building_sqft_total = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_detached_single_family = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_single_family_small_lot = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_single_family_large_lot = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_attached_single_family = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_multifamily_2_to_4 = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_multifamily_5_plus = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_retail_services = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_restaurant = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_accommodation = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_arts_entertainment = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_other_services = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_office_services = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_public_admin = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_education_services = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_medical_services = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_wholesale = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_transport_warehouse = models.DecimalField(max_digits=15, decimal_places=7, default=0) + building_sqft_industrial_non_warehouse = models.DecimalField(max_digits=15, decimal_places=7, default=0) + + residential_irrigated_square_feet = models.DecimalField(max_digits=15, decimal_places=7, default=0) + commercial_irrigated_square_feet = models.DecimalField(max_digits=15, decimal_places=7, default=0) + + softscape_and_landscape_percent = models.DecimalField(max_digits=15, decimal_places=7, null=True) + irrigated_percent = models.DecimalField(max_digits=15, decimal_places=7, default=0, null=True) + + # other fields not used for the core but useful in placetype visualization + percent_streets = models.DecimalField(max_digits=6, decimal_places=5, default=0) + percent_parks = models.DecimalField(max_digits=6, decimal_places=5, default=0) + percent_civic = models.DecimalField(max_digits=6, decimal_places=5, default=0) + percent_mixed_use = models.DecimalField(max_digits=6, decimal_places=5, default=0) + percent_residential = models.DecimalField(max_digits=6, decimal_places=5, default=0) + percent_employment = models.DecimalField(max_digits=6, decimal_places=5, default=0) + + pt_density = models.IntegerField(null=True) + pt_connectivity = models.IntegerField(null=True) + pt_land_use_mix = models.IntegerField(null=True) + pt_score = models.IntegerField(null=True) + + description = models.TextField(null=True, blank=True) + + intersections_sqmi = models.IntegerField(null=True) + avg_estimated_building_height_feet = models.IntegerField(null=True) + building_avg_number_of_floors = models.IntegerField(null=True) + block_avg_size_acres = models.IntegerField(null=True) + street_pattern = models.CharField(max_length=100, null=True) + + class Meta(object): + abstract = False + app_label = 'main' + + # Returns the string representation of the model. + def __unicode__(self): + return self.name + + def collect_built_form_attributes(self): + """ + Navigates the relational model describing built form and collects the critical data into a flat dictionary + describing all the attributes of a single built form, in a way that parallels the data required by the core v1 + :return: dict of built form attributes + """ + built_form = BuiltForm.objects.get_subclass(id=self.built_form_id) + + type = built_form.__class__.__name__ + key_prepend = 'b__' if type == 'PrimaryComponent' else 'bt__' if type == "PlacetypeComponent" else "pt__" + attributes = built_form.building_attributes + if not attributes: + raise Exception("No attributes for " + built_form) + built_form_dict = { + 'basic_attributes': { + 'name': built_form.name, + 'key': key_prepend + slugify(built_form.name).replace('-', '_'), + 'built_form_type': type, + 'gross_net_ratio': attributes.gross_net_ratio, + 'household_density': attributes.household_density, + 'population_density': attributes.gross_population_density, + 'intersection_density': built_form.intersection_density if isinstance(built_form, Placetype) else 0 + }, + 'density': defaultdict(float), + 'parcel_acres': defaultdict(float), + 'building_square_feet': defaultdict(float), + 'irrigation': defaultdict(float) + } + + # do parcel acres first + if built_form.__class__ in [PlacetypeComponent, PrimaryComponent]: + uses = list(attributes.buildingusepercent_set.all().values_list( + 'building_use_definition__name', flat=True)) + built_form_dict['parcel_acres'] = create_parcel_acres_dict(built_form, uses) + + if built_form.__class__ == Placetype: + + parcel_acres_dict = defaultdict(float) + + for component_percent in built_form.placetypecomponentpercent_set.filter(): + component = component_percent.component() + uses = component.building_attributes.buildingusepercent_set.all() + use_names = list(uses.values_list('building_use_definition__name', flat=True)) + component_parcel_acres_dict = create_parcel_acres_dict(component, use_names) + + for key, value in component_parcel_acres_dict.items(): + parcel_acres_dict[key] += value * float(component_percent.percent) + + built_form_dict['parcel_acres'] = parcel_acres_dict + + for use in attributes.buildingusepercent_set.all(): + use_name = use.building_use_definition.name.lower().replace(' ', '_') + built_form_dict['building_square_feet']['building_sqft_' + use_name] = use.gross_built_up_area + built_form_dict['density'][use_name + "_density"] = use.unit_density + + irrigation_dict = built_form_dict['irrigation'] + + irrigation_dict['softscape_and_landscape_percent'] = attributes.softscape_and_landscape_percent + irrigation_dict['irrigated_percent'] = attributes.irrigated_percent + + irrigation_dict['residential_irrigated_square_feet'] = attributes.residential_irrigated_square_feet or 0 + irrigation_dict['commercial_irrigated_square_feet'] = attributes.commercial_irrigated_square_feet or 0 + + return built_form, built_form_dict + + def update_attributes(self): + """ + updates the flat representation of a built form. should be executed whenever any part of the built form + has been changed + :return: + """ + + built_form, built_form_attributes_dict = self.collect_built_form_attributes() + + building_square_feet_dict = built_form_attributes_dict['building_square_feet'] + + building_square_feet_dict['building_sqft_industrial_non_warehouse'] = sum([ + float(building_square_feet_dict['building_sqft_manufacturing']), + float(building_square_feet_dict['building_sqft_construction_utilities']) + ]) + + building_square_feet_dict.pop('building_sqft_manufacturing') + building_square_feet_dict.pop('building_sqft_construction_utilities') + + building_square_feet_dict['building_sqft_total'] = sum( + # TODO sometimes value is null. It never should be + [float(value or 0) for key, value in building_square_feet_dict.items()] + ) + + building_square_feet_dict['building_sqft_detached_single_family'] = sum([ + float(building_square_feet_dict['building_sqft_single_family_large_lot']), + float(building_square_feet_dict['building_sqft_single_family_small_lot']) + ]) + + flat_row_dicts = dict( + built_form_attributes_dict['density'].items() + + built_form_attributes_dict['parcel_acres'].items() + + built_form_attributes_dict['basic_attributes'].items() + + building_square_feet_dict.items() + + built_form_attributes_dict['irrigation'].items() + ) + + for key, value in flat_row_dicts.items(): + setattr(self, key, value) + # self.save() + + # TODO some of these values are null and never should be + self.attached_single_family_density = self.attached_single_family_density or 0 + self.building_sqft_attached_single_family = self.building_sqft_attached_single_family or 0 + self.dwelling_unit_density = sum([self.single_family_large_lot_density or 0, + self.single_family_small_lot_density or 0, + self.attached_single_family_density, + self.multifamily_2_to_4_density or 0, + self.multifamily_5_plus_density or 0]) + + self.employment_density = sum([self.retail_density, self.office_density, + self.industrial_density, self.agricultural_density]) + + if self.built_form_type == 'Placetype': + self.run_placetype_metrics() + self.save() + + def run_placetype_metrics(self): + """ + calculate the non-core fields of the flat built form + """ + + built_form = BuiltForm.objects.get_subclass(id=self.built_form_id) + + placetype_component_percents = built_form.placetypecomponentpercent_set.all() + + civic_component_percents = placetype_component_percents.filter( + placetype_component__component_category__name__in=[Keys.BUILDINGTYPE_CIVIC, Keys.INFRASTRUCTURE_UTILITIES]) + self.percent_civic = civic_component_percents.aggregate(Sum('percent'))['percent__sum'] or 0 + + park_component_percents = placetype_component_percents.filter( + placetype_component__component_category__name=Keys.INFRASTRUCTURE_PARK) + self.percent_parks = park_component_percents.aggregate(Sum('percent'))['percent__sum'] or 0 + + street_component_percents = placetype_component_percents.filter( + placetype_component__component_category__name=Keys.INFRASTRUCTURE_STREET) + self.percent_streets = street_component_percents.aggregate(Sum('percent'))['percent__sum'] or 0 + + residential_component_percents = placetype_component_percents.filter( + placetype_component__component_category__name__in=Keys.RESIDENTIAL_BUILDINGTYPE_CATEGORIES) + self.percent_residential = residential_component_percents.aggregate(Sum('percent'))['percent__sum'] or 0 + + employment_component_percents = placetype_component_percents.filter( + placetype_component__component_category__name__in=Keys.EMPLOYMENT_BUILDINGTYPE_CATEGORIES) + self.percent_employment = employment_component_percents.aggregate(Sum('percent'))['percent__sum'] or 0 + + mixed_use_component_percents = placetype_component_percents.filter( + placetype_component__component_category__name=Keys.BUILDINGTYPE_MIXED_USE) + self.percent_mixed_use = mixed_use_component_percents.aggregate(Sum('percent'))['percent__sum'] or 0 + + #TODO all of these need to be weighted based on other scores + self.pt_density = self.get_pt_density() + self.pt_connectivity = self.get_pt_connectivity() + self.pt_land_use_mix = self.get_pt_land_use_mix() + + self.pt_score = int(round(float(self.pt_density)*0.3 + float(self.pt_connectivity)*0.4 + float(self.pt_land_use_mix)*0.3)) + + self.save() + #self.set_development_characteristics() + + def set_development_characteristics(self): + + non_civic_developable_pct = self.get_developable_percent(includes_civic=False) + civic_developable_pct = self.get_developable_percent(includes_civic=True) + + + #def get_developable_percent(self, includes_civic=True): + # + # percent_utilities = BuiltForm.objects.get(id=self.built_form_id).filter( + # placetype_component__component_category__name__in=[Keys.BUILDINGTYPE_CIVIC, Keys.INFRASTRUCTURE_UTILITIES])\ + # .aggregate(Sum('percent'))['percent__sum'] or 0 + # + # undevelopable = self.percent_parks + self.percent_streets + percent_utilities + (self.percent_civic if not includes_civic else 0) + # return 1 - undevelopable + # + #def get_jobs_density(self): + # pass + + def get_pt_density(self): + + raw_density = self.population_density + self.employment_density + + # Placetype.objects.all().order_by('building_attributes__gross_population_density').reverse()[9] + # .building_attributes.gross_population_density + tenth_largest = 11.6415251701 + + # Incidentally, largest = 91.3676228834, in case we wanted to use that instead? + + # Placetype.objects.all().order_by('building_attributes__gross_population_density')[0]\ + # .building_attributes.gross_population_density + smallest = 0 + + pt_density_weighted = float(raw_density) / (float(tenth_largest) - float(smallest)) + pt_density = pt_density_weighted if pt_density_weighted <= 10 else 10 + + return int(round(pt_density)) + + + def get_pt_connectivity(self): + + raw_connectivity = self.intersection_density + + # These values come from Placetypes.objects.all().aggregate(Max('intersection_density')) and 'Min' + # Staticly calculated here so that it doesn't have to get recomputed for each placetype + + min_intersections = 10 + max_intersections = 230 + + pt_connectivity = (float(raw_connectivity) / (float(max_intersections) - float(min_intersections)))*10 + + return int(round(pt_connectivity)) + + def get_pt_land_use_mix(self): + sqft_inst = float(self.building_sqft_public_admin) + float(self.building_sqft_education_services) + float(self.building_sqft_medical_services) + sqft_residential = float(self.building_sqft_attached_single_family) + float(self.building_sqft_detached_single_family) + float(self.building_sqft_multifamily_5_plus) + float(self.building_sqft_multifamily_2_to_4) + sqft_retail = float(self.building_sqft_retail_services) + float(self.building_sqft_arts_entertainment) + float(self.building_sqft_other_services) + sqft_office = float(self.building_sqft_office_services) + + sqft_total = sqft_inst + sqft_residential + sqft_retail + sqft_office + + if sqft_total >= 0.01: + for sqft in [sqft_inst, sqft_residential, sqft_retail, sqft_office]: + sqft = sqft if sqft > 0 else 0.01 + + percent_inst = sqft_inst/sqft_total + percent_residential = sqft_residential/sqft_total + percent_retail = sqft_retail/sqft_total + percent_office = sqft_office/sqft_total + + industrial_idx = math.log(percent_inst)*percent_inst if percent_inst else 0.0 + residential_idx = math.log(percent_residential)*percent_residential if percent_residential else 0.0 + retail_idx = math.log(percent_retail)*percent_retail if percent_retail else 0.0 + office_idx = math.log(percent_office)*percent_office if percent_office else 0.0 + + land_use_mix_index = ((industrial_idx + residential_idx + retail_idx + office_idx) / -math.log(4))*10 + return int(round(land_use_mix_index)) + else: + return 0 + + +def refresh_all_flat_built_forms(): + """ + clears the set of flat built forms and pulls the information over again + :return: + """ + FlatBuiltForm.objects.all().delete() + logger.info("exporting {0} placetypes, {1} buildingtypes, and {2} buildings".format(len(Placetype.objects.all()), + len(PlacetypeComponent.objects.all()), + len(PrimaryComponent.objects.all()))) + + for built_form in BuiltForm.objects.all().select_subclasses(): + if built_form.__class__ in [Placetype, PlacetypeComponent, PrimaryComponent]: + FlatBuiltForm(built_form_id=built_form.id).save() + for fbf in FlatBuiltForm.objects.all(): + fbf.update_attributes() + + # # Read in placetype descriptions and create a dictionary so you + descriptions_file = '%s/sproutcore/apps/fp/resources/Text/%s' % (settings.PROJECT_ROOT, 'placetype_descriptions.csv') + reader = csv.reader(open(descriptions_file, 'r')) + descriptions = {} + + for row in reader: + if row: + k,v = row + descriptions[k] = v + + for fbf in FlatBuiltForm.objects.all(): + fbf.description = descriptions[fbf.key] if descriptions.get(fbf.key) else "No description available" + fbf.save() + + bf_additional_attr_file = 'Place_Type_Additional_Attributes_Oct_31_2013.csv' + # # Read in placetype examples and create a dictionary so you + bf_additional_attr_path = '%s/sproutcore/apps/fp/resources/Text/%s' % (settings.PROJECT_ROOT, bf_additional_attr_file) + reader = csv.DictReader(open(bf_additional_attr_path, "rU")) + + #This dictionary has builtform id's as the key, and the value is another dictionary with various extra data + bf_additional_attr = {} + + for row in reader: + if row: + key = row["pt__key"] + bf_additional_attr[key] = row + + for fbf in FlatBuiltForm.objects.all(): + #Come back here and either add the name or the so it accesses dict b + if bf_additional_attr.get(fbf.key): + + fbf.intersections_sqmi = int(float(bf_additional_attr[fbf.key]["intersections_sqmi"])) + fbf.block_avg_size_acres = float(bf_additional_attr[fbf.key]["block_avg_size_acres"]) + fbf.street_pattern = bf_additional_attr[fbf.key]["street_pattern"] + fbf.avg_estimated_building_height_feet = int(float(bf_additional_attr[fbf.key]["avg_estimated_building_height_feet"])) + fbf.building_avg_number_of_floors = int(float(bf_additional_attr[fbf.key]["building_avg_number_of_Floors"])) + + fbf.save() + + +def create_parcel_acres_dict(built_form, uses): + parcel_acres_dict = defaultdict(float) + + if isinstance(built_form, PrimaryComponent) or isinstance(built_form, PlacetypeComponent): + parcel_acres = 1 + else: + parcel_acres = sum([ + primary_component.percent for primary_component in built_form.primary_components.all() + if primary_component.component_category.name not in Keys.INFRASTRUCTURE_TYPES + ]) + + employment_uses = [i for i in ['Office', 'Retail', 'Industrial', 'Agricultural'] if i in uses] + multifamily_uses = [i for i in [Keys.MULTIFAMILY_2_TO_4, Keys.MULTIFAMILY_5P] if i in uses] + + for base_use in ('Retail', 'Industrial', 'Office', 'Agricultural'): + if base_use in uses: + parcel_acres_dict['acres_parcel_employment_' + base_use.lower()] += parcel_acres + + # TODO : acres parcel single_family_residential isn't getting values + single_family_residential_uses = Keys.DETACHED_RESIDENTIAL + [Keys.ATTACHED_RESIDENTIAL] + for residential_use in single_family_residential_uses: + if residential_use in uses: + parcel_acres_dict['acres_parcel_residential_' + residential_use.lower().replace(' ', '_')] += parcel_acres + + if multifamily_uses: + parcel_acres_dict['acres_parcel_residential_multifamily'] += parcel_acres + + # if the buildingtype has a mix of multifamily residential and any kind of employment, + # add to the Mixed Use parcel acres + if multifamily_uses and employment_uses: + parcel_acres_dict['acres_parcel_mixed_use'] += parcel_acres + # if the buildingtype has any office, add to the mixed with office category + if 'Office' in employment_uses: + parcel_acres_dict['acres_parcel_mixed_use_with_office'] += parcel_acres + else: + parcel_acres_dict['acres_parcel_mixed_use_without_office'] += parcel_acres + + # if the buildingtype has a mix of employment types with no multifamily residential, + # add to the Mixed Employment parcel acres and to the Employment parcel acres + if employment_uses and not multifamily_uses: + parcel_acres_dict['acres_parcel_employment'] += parcel_acres + if len(employment_uses) > 1: + parcel_acres_dict['acres_parcel_employment_mixed'] += parcel_acres + + if 'Residential' in uses and not employment_uses: + parcel_acres_dict['acres_parcel_residential'] += parcel_acres + + return parcel_acres_dict diff --git a/footprint/main/models/built_form/infrastructure_attribute_set.py b/footprint/main/models/built_form/infrastructure_attribute_set.py new file mode 100644 index 000000000..1f5b4f624 --- /dev/null +++ b/footprint/main/models/built_form/infrastructure_attribute_set.py @@ -0,0 +1,42 @@ +__author__ = 'calthorpe_associates' + + +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . + +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from django.contrib.gis.db import models + + +class StreetAttributeSet(models.Model): + """ + Attributes of a :models:`main.Building`, :models:`main.BuildingType`, or :models:`main.Placetype`, + including a reference to its uses through :model:`built_form.building_use_percent.BuildingUsePercent`. + """ + objects = GeoInheritanceManager() + + class Meta(object): + abstract = False + app_label = 'main' + + def attributes(self): + return "building" + + lane_width = models.DecimalField(max_digits=8, decimal_places=4, default=0) + number_of_lanes = models.DecimalField(max_digits=8, decimal_places=4, default=0) + block_size = models.DecimalField(max_digits=8, decimal_places=4, default=0) + + + diff --git a/footprint/main/models/built_form/placetype.py b/footprint/main/models/built_form/placetype.py new file mode 100644 index 000000000..b87916dee --- /dev/null +++ b/footprint/main/models/built_form/placetype.py @@ -0,0 +1,61 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import logging + +from django.db import models +from django.db.models.aggregates import Sum + +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.building_aggregate import BuildingAttributeAggregate +from footprint.main.mixins.street_attributes import StreetAttributes + +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from footprint.main.models.built_form.built_form import BuiltForm + +__author__ = 'calthorpe_associates' +logger = logging.getLogger(__name__) + +# noinspection PySingleQuotedDocstring +class Placetype(BuildingAttributeAggregate, BuiltForm, StreetAttributes): + """ + Placetypes are a set of BuildingTypes with a percent mix applied to each BuildingType + """ + objects = GeoInheritanceManager() + + # So the model is pluralized correctly in the admin. + class Meta(BuiltForm.Meta): + verbose_name_plural = "Place Types" + app_label = 'main' + + placetype_components = models.ManyToManyField(PlacetypeComponent, through='PlacetypeComponentPercent') + + intersection_density = models.DecimalField(max_digits=8, decimal_places=4, default=0) + + def get_component_field(self): + return self.placetype_components + + def calculate_gross_net_ratio(self): + all_components = self.get_all_components().all() + net_components = all_components.filter(placetype_component__component_category__contributes_to_net=True) + + gross = all_components.aggregate(Sum('percent'))['percent__sum'] + net = net_components.aggregate(Sum('percent'))['percent__sum'] + # logger.debug("{0}: {1}/{2} ".format(self.name, net, gross)) + + return net / gross \ No newline at end of file diff --git a/footprint/main/models/built_form/placetype_component.py b/footprint/main/models/built_form/placetype_component.py new file mode 100644 index 000000000..beb0c78c5 --- /dev/null +++ b/footprint/main/models/built_form/placetype_component.py @@ -0,0 +1,68 @@ +# coding=utf-8 +from footprint.main.mixins.name import Name + +__author__ = 'calthorpe_associates' +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from django.db.models.signals import post_save + +from footprint.main.models.built_form.primary_component import PrimaryComponent +#from calthorpe.main.utils.subclasses import receiver_subclasses +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.building_aggregate import BuildingAttributeAggregate + +from built_form import on_collection_modify, BuiltForm + +class PlacetypeComponentCategory(Name): + contributes_to_net = models.BooleanField() + objects = GeoInheritanceManager() + + class Meta(object): + app_label = 'main' + + +class PlacetypeComponent(BuildingAttributeAggregate, BuiltForm): + """ + PlacetypeComponent represents a mix of PrimaryComponents, such as a "Rural Community College" or a "Boulevard" + """ + objects = GeoInheritanceManager() + primary_components = models.ManyToManyField(PrimaryComponent, through='PrimaryComponentPercent') + component_category = models.ForeignKey(PlacetypeComponentCategory) + + def get_component_field(self): + return self.__class__.primary_components + + class Meta(object): + app_label = 'main' + + def calculate_gross_net_ratio(self): + return 1 + + def get_parent_field(self): + return self.placetype_set + + +def on_instance_modify(sender, **kwargs): + instance = kwargs['instance'] + for parent_object in instance.get_parent_field().all(): + parent_object.aggregate_built_form_attributes() + + +post_save.connect(on_collection_modify, sender=PlacetypeComponent.primary_components.through) +post_save.connect(on_instance_modify, sender=PlacetypeComponent) diff --git a/footprint/main/models/built_form/placetype_component_percent.py b/footprint/main/models/built_form/placetype_component_percent.py new file mode 100644 index 000000000..33516f4ab --- /dev/null +++ b/footprint/main/models/built_form/placetype_component_percent.py @@ -0,0 +1,41 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.percent import Percent +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from django.db import models + +__author__ = 'calthorpe_associates' + +class PlacetypeComponentPercent(Percent): + """ + Many-to-many "through" class adds a percent field + """ + objects = GeoInheritanceManager() + placetype_component = models.ForeignKey(PlacetypeComponent, null=True) + placetype = models.ForeignKey('Placetype') + + class Meta(object): + app_label = 'main' + + def component(self): + return self.placetype_component + + def aggregate(self): + return self.placetype \ No newline at end of file diff --git a/footprint/main/models/built_form/primary_component.py b/footprint/main/models/built_form/primary_component.py new file mode 100644 index 000000000..2a9d64ffe --- /dev/null +++ b/footprint/main/models/built_form/primary_component.py @@ -0,0 +1,31 @@ +# coding=utf-8 +__author__ = 'calthorpe_associates' + +from django.db.models.signals import post_save +from django.db.models import Sum +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.built_form.built_form import BuiltForm + +class PrimaryComponent(BuiltForm): + """ + Building represents a template building, such as a Rural Community College + """ + objects = GeoInheritanceManager() + + class Meta(object): + # This is not abstract so that django can form a many-to-many relationship with it in built_form_set + app_label = 'main' + + def get_parent_field(self): + return self.placetypecomponent_set + + +def on_instance_modify(sender, **kwargs): + instance = kwargs['instance'] + for parent_object in instance.get_parent_field().all(): + if parent_object.primarycomponentpercent_set.all().aggregate(Sum('percent')) > .95: + parent_object.aggregate_built_form_attributes() + else: + pass + +post_save.connect(on_instance_modify, sender=PrimaryComponent) diff --git a/footprint/main/models/built_form/primary_component_percent.py b/footprint/main/models/built_form/primary_component_percent.py new file mode 100644 index 000000000..cd18d88c3 --- /dev/null +++ b/footprint/main/models/built_form/primary_component_percent.py @@ -0,0 +1,44 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager + +from footprint.main.mixins.percent import Percent +from django.db import models +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from footprint.main.models.built_form.primary_component import PrimaryComponent + +__author__ = 'calthorpe_associates' + +class PrimaryComponentPercent(Percent): + """ + Many-to-many "through" class adds a percent field + """ + objects = GeoInheritanceManager() + primary_component = models.ForeignKey(PrimaryComponent) + placetype_component = models.ForeignKey(PlacetypeComponent) + + class Meta(object): + app_label = 'main' + + def component(self): + return self.primary_component + + def aggregate(self): + return self.placetype_component + diff --git a/footprint/main/models/category.py b/footprint/main/models/category.py new file mode 100644 index 000000000..e69b71c0f --- /dev/null +++ b/footprint/main/models/category.py @@ -0,0 +1,32 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager + +__author__ = 'calthorpe_associates' + +class Category(models.Model): + objects = GeoInheritanceManager() + + key = models.CharField(max_length=100, null=False) + value = models.CharField(max_length=100, null=False) + class Meta: + app_label='main' + + + diff --git a/footprint/main/models/config/__init__.py b/footprint/main/models/config/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/config/config_entity.py b/footprint/main/models/config/config_entity.py new file mode 100644 index 000000000..ca6ac1496 --- /dev/null +++ b/footprint/main/models/config/config_entity.py @@ -0,0 +1,377 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. # +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from __builtin__ import classmethod +import logging +from django.contrib.auth.models import User + +from django.contrib.gis.db import models +import guppy +from footprint.main.lib.functions import flat_map, unique +from footprint.main.mixins.categories import Categories +from footprint.main.mixins.config_entity_related_collection_adoption import ConfigEntityRelatedCollectionAdoption +from footprint.main.mixins.config_entity_selection import ConfigEntitySelection +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.db_entities import DbEntities +from footprint.main.mixins.deletable import Deletable +from footprint.main.mixins.related_collection_adoption import RelatedCollectionAdoption +from footprint.main.mixins.scoped_key import ScopedKey +from footprint.main.mixins.built_form_sets import BuiltFormSets +from footprint.main.mixins.geographic_bounds import GeographicBounds +from footprint.main.mixins.name import Name +from footprint.main.mixins.policy_sets import PolicySets +from footprint.main.models.config.model_pickled_object_field import SelectionModelsPickledObjectField +from footprint.main.utils.utils import resolve_module_attr + +__author__ = 'calthorpe_associates' +logger = logging.getLogger(__name__) + +class ConfigEntity( + ConfigEntityRelatedCollectionAdoption, + ConfigEntitySelection, + RelatedCollectionAdoption, + GeographicBounds, + PolicySets, + BuiltFormSets, + DbEntities, + Name, + ScopedKey, + Categories, + Deletable): + """ + The base abstract class defining and UrbanFootprint object that is configurable + """ + + def __init__(self, *args, **kwargs): + super(ConfigEntity, self).__init__(*args, **kwargs) + if hasattr(self, 'parent_config_entity') and self.parent_config_entity and not self.bounds: + self.bounds = self.parent_config_entity.bounds + + objects = GeoInheritanceManager() + + # TODO this should be set to null=False and required + # The user who created the config_entity + creator = models.ForeignKey(User, null=True, related_name='config_entity_creator') + # The user who last updated the db_entity + updater = models.ForeignKey(User, null=True, related_name='config_entity_updater') + + media = models.ManyToManyField('Medium', null=True) + + # Use parent_config_entity_subclassed() to get the actual subclass, not a generic config_entity instance + parent_config_entity = models.ForeignKey('ConfigEntity', null=True, related_name='parent_set') + def parent_config_entity_subclassed(self): + return ConfigEntity.objects.get_subclass(id=self.parent_config_entity.id) if self.parent_config_entity else None + + # The optional config_entity whence this instance was cloned. + origin_config_entity = models.ForeignKey('ConfigEntity', null=True, related_name='clone_set') + def origin_config_entity_subclassed(self): + return ConfigEntity.objects.get_subclass(id=self.parent_config_entity.id) if self.parent_config_entity else None + + db = 'default' + + # The selected policy_set, built_form_set, and anything else that could conceivably be selected + # Currently selected sets are stored in a dictionary of the 'sets' key and selected DbEntities (when multiple DbEntities of the same key are present) are stored in a dictionary of the 'db_entities' key + selections = SelectionModelsPickledObjectField(default=lambda: {'sets': {}, 'db_entities': {}}) + + # Temporary state during post_save to disable recursing on post_save publishers + _no_post_save_publishing = False + # Temporary state during post_save to disable invoking the publishers upon db_entity_interest save + _no_post_save_db_entity_interest_publishing = False + + def donor(self): + """ + Used by the RelatedCollectionAdoption mixin + :return: + """ + return self.parent_config_entity_subclassed() + + # The collections listed here (defined in the various mixins) behave similarly. They are many-to-many collections. When computed_COLLECTION_NAME() is called, the returned values are either 1) if this instance has no items in its own collection, the values of the first ancestor via parent which has items set in its collection of the same name 2) if this instance has added items to its collection via add_COLLECTION_NAME(*items), then the parent's computed_COLLECTION_NAME() items are first inherited to this instance's collection (references, not clones), and then the items given are added. The end result is if no items exist for the instance, computed_COLLECTION_NAME returns the combined values of the ancestors. If items were added for the instance, computed_COLLECTION_NAME() returns the combined values of the ancestors followed by the instances own unique items. Duplicate items, as identified by the items' pk, are ignored by add_COLLECTION_NAME(*items) + # This pattern of collection inheritance is common enough that the functionality could be added to the Django model framework by extending certain classes, but I haven't invested the time in doing this. The logic resides in ConfigEntitySets for now + INHERITABLE_COLLECTIONS = ['db_entities', 'built_form_sets', 'policy_sets'] + # Because DbEntityInterest uses a through class (and others might in the future), this dict should be used to resolve the add and remove functions + + def __unicode__(self): + return '{0} ({1})'.format(self.name, self.__class__.__name__) + + class Meta(object): + abstract = False # False to allow multi-table-inheritance with many-to-many relationship + app_label = 'main' + + def children(self): + """ + Executes a query to return all children of this ConfigEntity. It's up to the caller to call the InheritanceManager.subclasses to 'cast' to the classes that they expect + :return: + """ + return ConfigEntity.objects.filter(parent_config_entity=self, deleted=False) + + def deleted_children(self): + return ConfigEntity.objects.filter(parent_config_entity=self, deleted=True) + + def parent_config_entity_saved(self): + """ + Called whenever the parent_config_entity's attributes are updated + :return: + """ + pass + + def config_keys(self): + return self.parent_config_entity.config_keys() + [self.key] if self.parent_config_entity else [self.key] + + def schema(self): + """ + The database schema name, created by concatinating this instance's key and its parents with underscores, where the top-most region is the first key, followed by sub-regions, project, and finally scenario + :return: underscore concatinated schema name + """ + return "__".join(self.config_keys()[1:]) if self.parent_config_entity else self.config_keys()[0] + + + def get_selected_db_entity(self, key): + """ + Returns the DbEntity with the given key that has been selected using _select_db_entity + :param key: + :return: + """ + db_entities = self.get_db_entities_by_key(key) + selection = self.selections['db_entities'].get(key, None) + if selection: + return selection + if len(db_entities) == 1: + return db_entities[0] + raise Exception( + "Instance {0} has no selected DbEntity for key {1} and there is not exactly one DbEntity with that key, rather {2}. Existing DbEntity keys: {3}".format( + self, + key, + len(db_entities), + map(lambda db_entity_interest: db_entity_interest.db_entity, self.computed_db_entity_interests()))) + + def get_db_entities_by_key(self, key): + """ + Gets the DbEntities of the instance with the given key. More than one DbEntity may have the same key in + the case that several alternate tables are available for a given function. Use get_selected_db_entity + to guarantee only one DbEntity is returned + + :param key: The key attribute of the DbEntity + :return: The mat return db_entities[0]ching DbEntities + """ + return self.computed_db_entities(key=key) + + def resolve_db_entity(self, table, schema=None): + """ + Search for a entity with the given table (and schema). + :param table: the name of the table + :param schema: the optional name of the DbEntity's schema. + :return: a DbEntity instance matching the table and schema belonging to the instance or the first ancestor that has it + """ + self._get('db_entities', table=table, schema=schema) + + + def resolve_db(self): + # This could be used to configure horizontal databases if needed + return 'default' + + def full_name(self): + """ + Concatinates all ancestor names except for that of the GlobalConfig. Thus a full name might be region_name subregion_name project_name scenario_name + """ + return ' '.join(self.parent_config_entity.full_name().extend([self.key])[1:]) + + def feature_class_of_base_class(self, base_class): + """ + Finds the feature_class of this config_entity that is derived from the given base class + :param base_class: + :return: + """ + db_entities = filter(lambda db_entity: issubclass( + # Get the configured abstract class + # The class_key is a hack to prevent Result db_entities from being chosen + resolve_module_attr(db_entity.feature_class_configuration.get('abstract_class', object)), + base_class) and not db_entity.class_key, + self.computed_db_entities()) + if len(db_entities) != 1: + raise Exception( + "Expected exactly one db_entity matching the base_class {0} but got {1}".format(base_class, + db_entities if len(db_entities) > 0 else 'none')) + return self.feature_class_of_db_entity_key(db_entities[0].key) + + def db_entity_feature_class(self, key, abstract_class=False, base_class=False): + """ + Resolves the Feature class subclass of this config_entity for the given key, or the base class version + if base_class-True + :param key: + :param abstract_class, Default False, if True returns the abstract base class instead of the subclass + :param base_class, Default False, if True returns the base class instead of the default rel class + :return: + """ + db_entity = self.computed_db_entities().get(key=key) + class_key = db_entity.class_key if db_entity.class_key else key + # Resolve the class_key of the DbEntity or just use key + from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator + if abstract_class: + subclass = resolve_module_attr(db_entity.feature_class_configuration['abstract_class']) + elif base_class: + subclass = FeatureClassCreator(self, db_entity).dynamic_feature_class(base_only=True) + else: + subclass = FeatureClassCreator(self, db_entity).dynamic_feature_class() + if not subclass: + raise Exception( + "For config_entity {0} no class associated with db_entity_key {1}{2}. Register a table in default_db_entity_configurations() of the owning config_entity.". + format(unicode(self), key, ", even after resolving class_key to {0}".format(class_key) if class_key != key else '')) + return subclass + + def clone_db_entities(self): + """ + If this ConfigEntity has an origin_config_entity, update or create the former's db_entity by copying the + latter's, if they are not already present. + :return: + """ + if not self.origin_config_entity: + raise Exception("Cannot clone a config_entity's db_entities which lacks an origin_config_entity") + origin_db_entity_interests = self.origin_config_entity_subclassed.computed_db_entity_interests + + # Sync the db_entities, instructing the method to clone from the origin + # TODO figure this out + #sync_db_entities() + + def feature_class_of_db_entity_key(self, key): + """ + If a db_entity maps to a base model class, this method will create a subclass to map the db_entry's table. + This is needed since tables of the same base type occur multiple times in the database and are created + dynamically in the scope of a ConfigEntity + The created class can then be used as a normal model class to query for instances and persist instances + The class is always given a reference to the config_entity instance in case queries need to join with it. + Returns the dynamic subclass associated with a given DbEntity table + Important: For DbEntities adopted from the parent_config_entity, the generated table and subclass are in + the context of the parent_config_entity or whatever ancestor defined the DbEntity. + The adopting config_entity (self) doesn't get its own table and class unless it specifically clones the + table (which isn't supported yet but easily could be.) + :param key: The key and table name of the DbEntity instance + :return: the dynamically created subclass + """ + return self.db_entity_feature_class(key) + + def has_feature_class(self, db_entity_key): + try: + self.feature_class_of_db_entity_key(db_entity_key) + return True + except Exception: + return False + + def abstract_class_of_db_entity(self, key): + """ + Like feature_class_of_db_entity, but returns the base abstract Feature class instead of the subclass + :param key: + :return: + """ + return self.db_entity_feature_class(key, abstract_class=True) + + + @classmethod + def lineage(cls, discovered=[]): + """ + Returns the hierarchy of parent classes of this class. Duplicates are ignored. Order is from this class up + until GlobalConfig + :param cls: + :return: + """ + return unique([cls] + flat_map(lambda parent_class: parent_class.lineage(discovered + cls.parent_classes()), + filter(lambda parent_class: parent_class not in discovered, + cls.parent_classes()))) + + def db_entity_owner(self, db_entity): + """ + Returns the ConfigEntity that owns the db_entity, either self or one of its ancestors + :param db_entity: + :return: + """ + return self if self.schema() == db_entity.schema else self.parent_config_entity_subclassed().db_entity_owner( + db_entity) + + def owned_db_entities(self, **query_kwargs): + """ + Returns non-adopted DbEntity instances + """ + return map(lambda db_entity_interest: db_entity_interest.db_entity, + self.owned_db_entity_interests(**query_kwargs)) + + def owned_db_entity_interests(self, **query_kwargs): + """ + Returns non-adopted DbEntityInterest instances + """ + return filter(lambda db_entity_interest: db_entity_interest.db_entity.schema == self.schema(), self._computed('db_entities', **query_kwargs)) + + def expect_parent_config_entity(self): + if not self.parent_config_entity: + raise Exception("{0} requires a parent_config_entity of types(s)".format( + self.__class__.__name__, + ', '.join(self.__class__.parent_classes()))) + + @property + def subclassed_config_entity(self): + """ + Resolves the config_entity to its subclass version. This garbage should all be done elegantly by Django, + maybe in the newest version. Otherwise refactor to generalize + :return: + """ + return ConfigEntity._subclassed_config_entity(self) + + _subclassed_config_entity_lookup = {} + + @classmethod + def _subclassed_config_entity(cls, config_entity): + """ + Cache subclassed config_entities to compensate for Djangos apparent inability to store these on models with ConfigEntity ForeignKeys + :param id: + :return: + """ + id = config_entity.id + return cls._subclassed_config_entity_by_id(id) + + @classmethod + def _subclassed_config_entity_by_id(cls, id): + subclassed_config_entity = cls._subclassed_config_entity_lookup.get(id, None) + if not subclassed_config_entity: + cls._subclassed_config_entity_lookup[id] = cls.resolve_scenario( + ConfigEntity.objects.get_subclass(id=id)) + return cls._subclassed_config_entity_lookup[id] + + # Cache for the instance's dynamically models + _dynamic_models_created = False + + @staticmethod + def resolve_scenario(config_entity): + for scenario_type in ['basescenario', 'futurescenario']: + if hasattr(config_entity, scenario_type): + return getattr(config_entity, scenario_type) + return config_entity + + # System memory usage (nothing to do with ConfigEntity) + _heapy = None + @classmethod + def init_heapy(cls): + ConfigEntity._heapy = guppy.hpy() + + @classmethod + def dump_heapy(cls): + # Print memory statistics + print cls._heapy.heap() + # Print relative memory consumption w/heap traversing + print cls._heapy.heap().get_rp(40) + + @classmethod + def start_heapy_diagnosis(cls): + cls._heapy.setrelheap() diff --git a/footprint/main/models/config/db_entity_interest.py b/footprint/main/models/config/db_entity_interest.py new file mode 100644 index 000000000..b6d3a9ae2 --- /dev/null +++ b/footprint/main/models/config/db_entity_interest.py @@ -0,0 +1,62 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.managers.config.db_entity_interest_manager import DbEntityInterestManager +from footprint.main.mixins.deletable import Deletable +from footprint.main.models.geospatial.db_entity import DbEntity +from footprint.main.models.config.interest import Interest +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator + +__author__ = 'calthorpe_associates' + +class DbEntityInterest(Deletable): + objects = DbEntityInterestManager() + + # A class name is used to avoid circular dependency + config_entity = models.ForeignKey('ConfigEntity', null=False) + db_entity = models.ForeignKey(DbEntity, null=False) + interest = models.ForeignKey(Interest, null=False) + + @property + def feature_fields(self): + """ + The fields of the DbEntity's Feature class, if one exists + """ + feature_class_creator = FeatureClassCreator(self.config_entity, self.db_entity) + if not feature_class_creator.feature_class_is_ready: + return [] + feature_class = feature_class_creator.dynamic_feature_class() + return feature_class_creator.dynamic_feature_class().objects.all().result_fields_and_title_lookup()[0] if feature_class else [] + + @property + def feature_field_title_lookup(self): + """ + The fields to title lookup of the DbEntity's Feature class, if one exists + """ + feature_class_creator = FeatureClassCreator(self.config_entity, self.db_entity) + if not feature_class_creator.feature_class_is_ready: + return {} + feature_class = feature_class_creator.dynamic_feature_class() + return feature_class_creator.dynamic_feature_class().objects.all().result_fields_and_title_lookup()[1] if feature_class else {} + + def __unicode__(self): + return "ConfigEntity:{0}, DbEntity:{1}, Interest:{2}".format(self.config_entity, self.db_entity, self.interest) + + class Meta(object): + app_label = 'main' diff --git a/footprint/main/models/config/global_config.py b/footprint/main/models/config/global_config.py new file mode 100644 index 000000000..8dbc201e7 --- /dev/null +++ b/footprint/main/models/config/global_config.py @@ -0,0 +1,67 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.main.managers.geo_inheritance_manager import FootprintGeoManager +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + + +class GlobalConfig(ConfigEntity): + """ + A singleton whose adoptable attributes are adopted by other ConfigEntity instances + """ + + objects = FootprintGeoManager() + + def __init__(self, *args, **kwargs): + super(GlobalConfig, self).__init__(*args, **kwargs) + self.key = Keys.GLOBAL_CONFIG_KEY + self.name = Keys.GLOBAL_CONFIG_NAME + + def full_name(self): + """ + Overrides the default and return name + """ + return self.name + + def db_entity_owner(self, db_entity): + if self.schema() == db_entity.schema: + return self + raise Exception("Reached GlobalConfig without finding an owner for the db_entity {0}".format(db_entity)) + + @classmethod + def parent_classes(cls): + """ + GlobalConfig can not have a parent + """ + return [] + + class Meta(object): + app_label = 'main' + + +def global_config_singleton(): + """ + Returns the lone GlobalConfig, throwing an ObjectNotFound Exception if it hasn't yet been created + :return: + """ + return GlobalConfig.objects.get(key=Keys.GLOBAL_CONFIG_KEY) + diff --git a/footprint/main/models/config/interest.py b/footprint/main/models/config/interest.py new file mode 100644 index 000000000..bef3217b2 --- /dev/null +++ b/footprint/main/models/config/interest.py @@ -0,0 +1,35 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.key import Key + +__author__ = 'calthorpe_associates' + + +class Interest(Key): + """ + An indication of ownership, dependency, follower, etc, to indicate a ConfigEntity's relationship to a DbEntity + """ + objects = GeoInheritanceManager() + + class Meta(object): + abstract = False + app_label = 'main' + diff --git a/footprint/main/models/config/model_pickled_object_field.py b/footprint/main/models/config/model_pickled_object_field.py new file mode 100644 index 000000000..ff9a184ff --- /dev/null +++ b/footprint/main/models/config/model_pickled_object_field.py @@ -0,0 +1,116 @@ +import logging +from picklefield import PickledObjectField +from picklefield.fields import dbsafe_decode, PickledObject, _ObjectWrapper +from footprint.main.lib.functions import map_dict_to_dict +from footprint.main.utils.utils import resolvable_model_name, resolve_model + +__author__ = 'calthorpe_associates' +logger = logging.getLogger(__name__) + + +class ModelPickledObjectField(PickledObjectField): + """ + Simplifies pickling of model instances by just storing the class name + and id, since the models are obviously stored elsewhere in the database + """ + + def pre_save(self, model_instance, add): + """ + Simplify the model instance to an id and class name to avoid + expensive pickling + """ + if model_instance.id == 0: + raise Exception("Attempt to pickle unsaved model instance: %s" % model_instance) + simple_dict = dict( + class_name=resolvable_model_name(model_instance.__class__), + id=model_instance.id + ) + return super(ModelPickledObjectField, self).pre_save(simple_dict, add) + + def to_python(self, value): + """ + Resolve the model from the class name and id + """ + simple_dict = super(ModelPickledObjectField, self).to_python(value) + if simple_dict: + return resolve_model(simple_dict['class_name']).objects.get(id=simple_dict['id']) + return simple_dict + + +class SelectionModelsPickledObjectField(PickledObjectField): + """ + Simplifies pickling of model instances by just storing the class name + and id, since the models are obviously stored elsewhere in the database + This expects a dict in the form + dict(sets=dict(policy_set=model, built_form_set=model), + db_entities=dict(foo_db_entity_key=foo_db_entity_model, ...)) + """ + + @staticmethod + def pk_of_model(model_instance): + if not model_instance: + return model_instance + if model_instance.pk == 0: + raise Exception("Can't pickle unsaved model instance: %s" % model_instance) + return model_instance.pk + + def get_db_prep_value(self, value, connection=None, prepared=False): + """ + Simplify the model instance to an id and class name to avoid + expensive pickling + """ + mapped_dict = map_dict_to_dict(lambda key, inner_dict: + [key, map_dict_to_dict(lambda inner_key, model_instance: + [inner_key, dict( + class_name=resolvable_model_name(model_instance.__class__), + pk=self.pk_of_model(model_instance) + )] if model_instance else None, # Should never be null, but sometimes i + inner_dict + )], + value) + return super(SelectionModelsPickledObjectField, self).get_db_prep_value(mapped_dict, connection, prepared) + + _model_cache = {} + + def model_from_class_name_and_pk(self, model_dict): + try: + model_class = self.__class__._model_cache.get(model_dict['class_name'], None) + if not model_class: + model_class = self.__class__._model_cache[model_dict['class_name']] = resolve_model(model_dict['class_name']) + return model_class.objects.get(pk=model_dict['pk']) + except: + logger.warn("pk %s of model class %s not found. This should not happen unless the model is being deleted" % (model_dict['pk'], model_dict['class_name'])) + return None + + def to_python(self, value): + """ + Resolve the model from the class name and id + """ + if value is not None: + try: + value = dbsafe_decode(value, self.compress) + except: + # If the value is a definite pickle; and an error is raised in + # de-pickling it should be allowed to propogate. + if isinstance(value, PickledObject): + raise + else: + # If the value was encoded (not from cache, convert it here back to our + # desired format) + if value: + if isinstance(value, _ObjectWrapper): + unwrapped_value = value._obj + else: + unwrapped_value = value + + return map_dict_to_dict(lambda key, inner_dict: + [key, map_dict_to_dict(lambda inner_key, model_dict: + [inner_key, + self.model_from_class_name_and_pk(model_dict)], + inner_dict + )], + unwrapped_value) + # Value from cache, leave alone + return value + + diff --git a/footprint/main/models/config/policy.py b/footprint/main/models/config/policy.py new file mode 100644 index 000000000..8ba401ec0 --- /dev/null +++ b/footprint/main/models/config/policy.py @@ -0,0 +1,84 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from picklefield import PickledObjectField +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.key import Key +from footprint.main.mixins.scoped_key import ScopedKey +from footprint.main.mixins.shared_key import SharedKey +from footprint.main.mixins.tags import Tags +from footprint.main.mixins.name import Name +from footprint.main.models.config.model_pickled_object_field import ModelPickledObjectField + +__author__ = 'calthorpe_associates' + +class PolicyLookup(object): + def policy_by_key(self, key_path): + """ + Retrieve a policy by key path. Recurses into the policies to match the given path. + :param key_path: + :return: The matching policy value or none + """ + keys = key_path.split('.') if key_path else [] + + if len(keys) == 0: + # The path resolved to a policy or policy_set + return self + + # Try to find a policy that matches the first key + child_policies = self.policies.filter(key=keys[0]) + if len(child_policies) != 1: + # If not found + if len(keys) == 1: + # Try to find a value that matches the key + return self.values.get(keys[0], None) + return None + # If found, recurse on the remaining keys + return child_policies[0].policy_by_key('.'.join(keys[1:])) + +class Policy(SharedKey, Name, Tags, PolicyLookup): + """ + A Policy is a loosely defined data structure. That represents a policy of a policy set. Policies may be shared across sets. Their semantic meaning may be determined by their shared key and they may be categorized by their tags. A policy has a range of possible values, anything from True/False to a number range or anything else that can be selected and have meaning. The range is serialized by the values attribute. Classes that have PolicySet attributes, namely ConfigEntity instances, should store the actual selected value of each Policy in a separate data structure ConfigEntity instances store policy settings in ConfigEntity.selections.policy_sets. See that attribute to understand how policy value selections are stored. + + """ + schema = models.CharField(max_length=100, null=True) + objects = GeoInheritanceManager() + policies = models.ManyToManyField('Policy', default=lambda: []) + # Pickle the set of values into a single string field + # The allowed values of the policy. This should be anything that can be serialized and represented on the client + values = PickledObjectField() + + def update_or_create_policy(self, policy_config): + child_policy = Policy.objects.update_or_create( + key=policy_config['key'], + schema='%s__%s' % (self.schema, policy_config['key']) if self.schema else policy_config['key'], + defaults=dict( + name=policy_config['name'], + description=policy_config.get('description', None), + values=policy_config. get('values', {}) + ))[0] + if policy_config.get('policies', None) and len(policy_config['policies']) > 0: + child_policy.policies.add(*map(lambda child_policy_config: + child_policy.update_or_create_policy(child_policy_config), policy_config['policies'])) + return child_policy + + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/config/policy_set.py b/footprint/main/models/config/policy_set.py new file mode 100644 index 000000000..96bddadf0 --- /dev/null +++ b/footprint/main/models/config/policy_set.py @@ -0,0 +1,41 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.deletable import Deletable + +from footprint.main.models.config.policy import Policy, PolicyLookup +from footprint.main.mixins.key import Key +from footprint.main.mixins.name import Name + + +__author__ = 'calthorpe_associates' + + +class PolicySet(Key, Name, PolicyLookup, Deletable): + """ + A policy set is a list of policies, which may themselves embed policies. PolicySet also defines an attributes object to store arbitrary information about the policy set + """ + objects = GeoInheritanceManager() + policies = models.ManyToManyField(Policy) + + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/config/project.py b/footprint/main/models/config/project.py new file mode 100644 index 000000000..6e96952cc --- /dev/null +++ b/footprint/main/models/config/project.py @@ -0,0 +1,58 @@ +# coding=utf-8 +from django.contrib.gis.geos import Polygon, MultiPolygon +from django.db import models +from django.conf import settings + +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.config.region import Region +from footprint.main.models.config.config_entity import ConfigEntity + +__author__ = 'calthorpe_associates' + + +class Project(ConfigEntity): + """ + A Project references a single Region and serves as the parent configuration for one or more Scenarios + """ + objects = GeoInheritanceManager() + + base_year = models.IntegerField(default=2005) + + def __init__(self, *args, **kwargs): + super(Project, self).__init__(*args, **kwargs) + self.srid = settings.DEFAULT_SRID + + def recalculate_bounds(self): + authority_feature_classes = [self.feature_class_of_db_entity_key(db_entity.key) + for db_entity in self.computed_db_entities() if db_entity.extent_authority] + extents = [] + for authority_feature_class in authority_feature_classes: + all_features = authority_feature_class.objects.all() + if len(all_features) > 0: + bounds = all_features.extent_polygon() + extents.append(bounds) + self.bounds = MultiPolygon(extents) + # Disable publishers for this simple update + self._no_post_save_publishing = True + self.save() + self._no_post_save_publishing = False + else: + pass + + def region(self): + return self.parent_config_entity + + def save(self, force_insert=False, force_update=False, using=None): + super(Project, self).save(force_insert, force_update, using) + + @classmethod + def parent_classes(cls): + """ + Projects may only have a Region for a parent + :param cls: + :return: + """ + return [Region] + + class Meta(object): + app_label = 'main' \ No newline at end of file diff --git a/footprint/main/models/config/region.py b/footprint/main/models/config/region.py new file mode 100644 index 000000000..501df988c --- /dev/null +++ b/footprint/main/models/config/region.py @@ -0,0 +1,42 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.config.global_config import GlobalConfig, global_config_singleton +from footprint.main.models.config.config_entity import ConfigEntity + +__author__ = 'calthorpe_associates' + + +class Region(ConfigEntity): + """ + The Region may have a parent Region. + """ + objects = GeoInheritanceManager() + + def __init__(self, *args, **kwargs): + super(Region, self).__init__(*args, **kwargs) + self.parent_config_entity = self.parent_config_entity or global_config_singleton() + + def save(self, force_insert=False, force_update=False, using=None): + super(Region, self).save(force_insert, force_update, using) + + @classmethod + def parent_classes(cls): + return [GlobalConfig] + + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/config/scenario.py b/footprint/main/models/config/scenario.py new file mode 100644 index 000000000..6e4443f2c --- /dev/null +++ b/footprint/main/models/config/scenario.py @@ -0,0 +1,103 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from tastypie import bundle +from footprint.main.lib.functions import get_first +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.models.config.project import Project + +__author__ = 'calthorpe_associates' + +class Scenario(ConfigEntity): + """ + ProjectScenario is a temporary name while the old Scenario class exists + Scenarios configure future conditions relatives to the base conditions of their project + """ + objects = GeoInheritanceManager() + + year = models.IntegerField(null=False, blank=False) + + def set_parent_config_entity(self): + self.bounds = self.parent_config_entity.bounds + + def save(self, force_insert=False, force_update=False, using=None): + self.expect_parent_config_entity() + super(Scenario, self).save(force_insert, force_update, using) + + @property + def project(self): + return Project.objects.get(id=self.parent_config_entity.id) + + def full_name(self): + """ + Concatinates all ancestor names except for that of the GlobalConfig and adds self.name. Scenario also + includes the year + """ + return ' '.join(self.parent_config_entity.full_name().extend([self.name, self.year])[1:]) + + @classmethod + def parent_classes(cls): + """ + Scenarios may only have Projects as a parent + :param cls: + :return: + """ + return [Project] + + @property + def analytic_modules(self): + from footprint.main.models.analysis_module.core_module.core import Core + from footprint.main.models.analysis_module.fiscal_module.fiscal import Fiscal + from footprint.main.models.analysis_module.vmt_module.vmt import Vmt + return dict( + core=get_first(Core.objects.filter(config_entity=self), None), + fiscal=get_first(Fiscal.objects.filter(config_entity=bundle.obj), None), + vmt=get_first(Vmt.objects.filter(config_entity=bundle.obj), None) + ) + @analytic_modules.setter + def analysis_modules(self, value): + pass + + class Meta(object): + + # Make abstract = False so that a Scenario table is created to store common Scenario attributes + # Callers may also choose to deal with Scenarios generally and not with the subclasses + abstract = False + app_label = 'main' + +class BaseScenario(Scenario): + """ + BaseScenarios represent an editing of primary or BaseFeature data. + """ + objects = GeoInheritanceManager() + class Meta(object): + abstract = False + app_label = 'main' + +class FutureScenario(Scenario): + """ + FutureScenarios represent and editing of a BuiltFormFeature table + that is derived from an UrbanFootprint BaseFeature table + """ + objects = GeoInheritanceManager() + class Meta(object): + abstract = False + app_label = 'main' + diff --git a/footprint/main/models/constants.py b/footprint/main/models/constants.py new file mode 100644 index 000000000..ce18df019 --- /dev/null +++ b/footprint/main/models/constants.py @@ -0,0 +1,43 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from decimal import Decimal + +__author__ = 'calthorpe_associates' + + +class Constants(object): + USE_EFFICIENCY_DEFAULT = .85 + + RESIDENTIAL_SQUARE_FEET_PER_DWELLING_UNIT = 1 + OFFICE_SQUARE_FEET_PER_EMPLOYEE = 1 + RETAIL_SQUARE_FEET_PER_EMPLOYEE = 1 + INDUSTRIAL_SQUARE_FEET_PER_EMPLOYEE = 1 + + HH1_PERCENT = .4 + HH2_PERCENT = .2 + HH3_PERCENT = .2 + HH4_PERCENT = .1 + HH5P_PERCENT = .1 + + AVERAGE_HH_SIZE = 2.5 + + VACANCY_RATE = 0 + + SQUARE_FEET_PER_ACRE = Decimal(43560.00000) + PARKING_STALL_SQUARE_FEET = Decimal(330.00000) \ No newline at end of file diff --git a/footprint/main/models/database/__init__.py b/footprint/main/models/database/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/database/information_schema.py b/footprint/main/models/database/information_schema.py new file mode 100644 index 000000000..77a423b8f --- /dev/null +++ b/footprint/main/models/database/information_schema.py @@ -0,0 +1,309 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import urllib2 +from django.core.exceptions import ObjectDoesNotExist +from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection +from django.middleware import transaction +import psycopg2 +from footprint import settings +from footprint.common.utils.postgres_utils import pg_connection_parameters + +from footprint.main.managers.database.managers import InformationSchemaManager, PGNamespaceManager +from django.db import models, connections, connection +from footprint.main.utils.utils import parse_schema_and_table +#TODO move this to utils +from footprint.uf_tools import executeSQL_now +from logging import getLogger +logger = getLogger(__name__) +__author__ = 'calthorpe_associates' + +class InformationSchema(models.Model): + + table_catalog = models.CharField(max_length = 100) + table_schema = models.CharField(max_length = 100) + table_name = models.CharField(max_length = 100) + # Pretend this is the primary key since the table doesn't have a single column primary key + column_name = models.CharField(max_length = 100, null=False, primary_key=True) + data_type = models.CharField(max_length = 100) + udt_name = models.CharField(max_length = 100, null=False, primary_key=True) + + objects = InformationSchemaManager() + + def __unicode__(self): + return "Catalog: {0}, Schema: {1}, Table: {2}, Column: {3}, Type: {4}".format(self.table_catalog, self.table_schema, self.table_name, self.column_name, self.data_type) + + def full_table_name(self): + return "{0}.{1}".format(self.table_schema, self.table_name) + + @classmethod + def add_column_conditionally(cls, schema, table, column_name, column_type, primary_key=False, create_primary_key_duplicate_column=None): + """ + Adds the column of the given type to the given table if absent + :param schema: The database schema name + :param table: The table name + :param column_name: The name of the column + :param column_type: e.g. 'varchar'. This is not used by primary keys + :param primary_key: Default False, set True to make the new column the primary key + :param create_primary_key_duplicate_column: creates a primary key from this name based on column_name's values + :return: True if created, otherwise False + """ + full_tablename = '"{schema}"."{table}"'.format(schema=schema, table=table) + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + if not InformationSchema.objects.has_column(schema, table, create_primary_key_duplicate_column): + if not primary_key: + alter_source_id_sql = 'alter table {full_tablename} add column {column_name} {column_type};'.format( + full_tablename=full_tablename, column_name=column_name, column_type=column_type) + cursor.execute(alter_source_id_sql) + else: + existing_primary_key = InformationSchema.get_primary_key_name(schema, table) + if existing_primary_key: + # If a primary_key of a different name already exists, just rename it + target_name = create_primary_key_duplicate_column or column_name + if existing_primary_key != target_name: + alter_source_id_sql = 'alter table {full_tablename} rename column {existing_primary_key} to {target_name}'.format( + full_tablename=full_tablename, existing_primary_key=existing_primary_key, target_name=target_name) + cursor.execute(alter_source_id_sql) + + elif create_primary_key_duplicate_column: + # Create a new primary key column, create_primary_key_duplicate_column, by copying the values from column_name and casting to integer + create_column_sql = 'alter table {full_tablename} add column {duplicate_column_name} integer'.format( + full_tablename=full_tablename, duplicate_column_name=create_primary_key_duplicate_column) + update_sql = 'update {full_tablename} set {duplicate_column_name} = cast({column_name} AS integer)'.format( + full_tablename=full_tablename, duplicate_column_name=create_primary_key_duplicate_column, column_name=column_name) + alter_source_id_sql = 'alter table {full_tablename} add constraint {table}_{schema}_{duplicate_column_name}_pk primary key ({duplicate_column_name})'.format( + full_tablename=full_tablename, table=table, schema=schema, column_name=column_name, duplicate_column_name=create_primary_key_duplicate_column) + cursor.execute(create_column_sql) + cursor.execute(update_sql) + cursor.execute(alter_source_id_sql) + else: + alter_source_id_sql = 'alter table {full_tablename} add column {column_name} serial primary key'.format( + full_tablename=full_tablename, column_name=column_name) + cursor.execute(alter_source_id_sql) + return True + return False + + @classmethod + def get_primary_key_name(cls, schema, table): + """ + Uses the inspection code to find the primary key column name, if one exists + :param schema: + :param table: + :return: The primary key name or None + """ + + connection = connections['default'] + cursor = connection.cursor() + table_name = '"{schema}"."{table}"'.format(schema=schema, table=table) + + # Use our own class to make up for lack of schema support in table queries + smart_database_introspection = SmartDatabaseIntrospection(connection) + try: + indexes = smart_database_introspection.get_indexes(cursor, table_name) + except NotImplementedError: + indexes = {} + + # Fill this dict with field definitions + for i, row in enumerate(smart_database_introspection.get_table_description(cursor, table_name)): + column_name = row[0] + # Add primary_key and unique, if necessary. + if column_name in indexes: + if indexes[column_name]['primary_key']: + return column_name + + + class Meta(object): + db_table = '"information_schema"."columns"' + +class PGNamespace(models.Model): + """ + This class is just needed to list schemas and see if they exist if they have no tables + """ + # Pretend this is the primary key since the table doesn't have a single column primary key + nspname = models.CharField(max_length = 100, null=False, primary_key=True) + objects = PGNamespaceManager() + + class Meta(object): + db_table = 'pg_namespace' + + +class SouthMigrationHistory(models.Model): + """ + This class is just needed to list schemas and see if they exist if they have no tables + """ + # Pretend this is the primary key since the table doesn't have a single column primary key + id = models.IntegerField(null=False, primary_key=True) + app_name = models.CharField(max_length=100) + migration = models.CharField(max_length=100) + applied = models.DateTimeField() + + class Meta(object): + db_table = 'south_migrationhistory' + + +class SpatialRefSys(models.Model): + proj4text = models.CharField(max_length=2048) + srtext = models.CharField(max_length=2048) + auth_srid = models.IntegerField() + auth_name = models.CharField(max_length=2048) + srid = models.IntegerField(primary_key=True) + + class Meta(object): + db_table = 'spatial_ref_sys' + + +class GeometryColumns(models.Model): + + f_table_catalog = models.CharField(max_length=256, null=False) + f_table_schema = models.CharField(max_length=256, null=False, primary_key=True) + f_table_name = models.CharField(max_length=256, null=False, primary_key=True) + f_geometry_column = models.CharField(max_length=256, null=False, primary_key=True) + coord_dimension = models.IntegerField(null=False) + srid = models.IntegerField(null=False) + type = models.CharField(max_length=30, null=False) + + class Meta(object): + db_table = 'geometry_columns' + + +def sync_geometry_columns(schema=None, table=None): + """ + Adds one or more entries to the PostGIS geometry_columns + :param schema: Optional database schema to which to limit search + :param table: Optional table name to which to limit search + :return: + """ + tables_with_geometry = InformationSchema.objects.tables_with_geometry(schema=schema, table=table) + for information_scheme in tables_with_geometry: + + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + sql = "select ST_CoordDim({2}), ST_SRID({2}), ST_GeometryType({2}) from {1}.{0}".format(information_scheme.table_name, information_scheme.table_schema, information_scheme.column_name) + ret = cursor.execute(sql) + if ret and len(ret) > 0: + coord, srid, geom_type = ret[0] + else: + coord, srid, geom_type = (2, 4326, 'GEOMETRY') + geometry_record, new_record = GeometryColumns.objects.get_or_create( + f_table_name=information_scheme.table_name, + f_geometry_column=information_scheme.column_name, + f_table_schema=information_scheme.table_schema, + defaults=dict( + coord_dimension=coord, + srid=srid, + type=geom_type, + )) + if not new_record: + geometry_record.coord_dimension = coord + geometry_record.srid = srid + geometry_record.type = geom_type + geometry_record.save() + + +def scrape_insert_from_spatialreference(authority, srid): + address = "http://www.spatialreference.org/ref/{1}/{0}/postgis/".format(srid, authority) + logger.debug('Looking up {authority}:{srid}'.format(srid=srid, authority=authority)) + try: + return urllib2.urlopen(address).read() + except: + logger.warn('Could not find SRID {srid}!'.format(srid=srid)) + return None + + +def verify_srid(srid): + try: + srs = SpatialRefSys.objects.get(auth_srid=int(srid)) + logger.debug("Using SRID: " + srid) + return srs + except ObjectDoesNotExist: + pass + + insert = scrape_insert_from_spatialreference('esri', srid) + if insert: + logger.info("Inserting {srid} into spatial_ref_sys table".format(srid=srid)) + logger.debug(insert) + connection.cursor().execute(insert) + srs = SpatialRefSys.objects.filter(auth_srid=int(srid)) + if srs.count(): + return srs[0] + + return False + + +class SmartDatabaseIntrospection(DatabaseIntrospection): + + def get_table_description(self, cursor, full_table_name): + """ + Override the parent method to take schemas into account, sigh + :param cursor: + :param full_table_name: + :return: + """ + schema, table = parse_schema_and_table(full_table_name) + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + cursor.execute(""" + SELECT column_name, is_nullable + FROM information_schema.columns + WHERE table_name = %s and table_schema = %s""", [table, schema]) + null_map = dict(cursor.fetchall()) + cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(full_table_name)) + return [tuple([item for item in line[:6]] + [null_map[line[0]]==u'YES']) + for line in cursor.description] + + + def get_indexes(self, cursor, table_name): + """ + OVERRIDDEN to work with schemas, sigh + + Returns a dictionary of fieldname -> infodict for the given table, + where each infodict is in the format: + {'primary_key': boolean representing whether it's the primary key, + 'unique': boolean representing whether it's a unique index} + """ + schema, table = parse_schema_and_table(table_name) + # This query retrieves each index on the given table, including the + # first associated field name + cursor.execute(""" + SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary + FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, + pg_catalog.pg_index idx, pg_catalog.pg_attribute attr, + information_schema.columns isc + WHERE c.oid = idx.indrelid + AND idx.indexrelid = c2.oid + AND attr.attrelid = c.oid + AND attr.attnum = idx.indkey[0] + AND c.relname = %s + AND c.relname = isc.table_name + AND isc.table_schema = %s + AND isc.column_name = attr.attname + """, [table, schema]) + indexes = {} + for row in cursor.fetchall(): + # row[1] (idx.indkey) is stored in the DB as an array. It comes out as + # a string of space-separated integers. This designates the field + # indexes (1-based) of the fields that have indexes on the table. + # Here, we skip any indexes across multiple fields. + if ' ' in row[1]: + continue + indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]} + return indexes diff --git a/footprint/main/models/future/__init__.py b/footprint/main/models/future/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/future/core_end_state_demographic_feature.py b/footprint/main/models/future/core_end_state_demographic_feature.py new file mode 100644 index 000000000..f3e9ffcea --- /dev/null +++ b/footprint/main/models/future/core_end_state_demographic_feature.py @@ -0,0 +1,51 @@ +from footprint.main.models.geospatial.feature import UpdatingFeature +from django.db import models + +__author__ = 'calthorpe_associates' + + +class CoreEndStateDemographicFeature(UpdatingFeature): + du_occupancy_rate = models.DecimalField(max_digits=14, decimal_places=4) + pop_male = models.DecimalField(max_digits=14, decimal_places=4) + pop_female = models.DecimalField(max_digits=14, decimal_places=4) + + pop_avg_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + + pop_female_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + pop_male_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + + pop_age16_up = models.DecimalField(max_digits=14, decimal_places=4) + pop_age25_up = models.DecimalField(max_digits=14, decimal_places=4) + pop_age65_up = models.DecimalField(max_digits=14, decimal_places=4) + + pop_age20_64 = models.DecimalField(max_digits=14, decimal_places=4) + pop_hs_not_comp = models.DecimalField(max_digits=14, decimal_places=4) + pop_hs_diploma = models.DecimalField(max_digits=14, decimal_places=4) + pop_some_college = models.DecimalField(max_digits=14, decimal_places=4) + pop_college_degree = models.DecimalField(max_digits=14, decimal_places=4) + pop_graduate_degree = models.DecimalField(max_digits=14, decimal_places=4) + pop_employed = models.DecimalField(max_digits=14, decimal_places=4) + + hh_inc_00_10 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_10_20 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_20_30 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_30_40 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_40_50 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_50_60 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_60_75 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_75_100 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_100_125 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_125_150 = models.DecimalField(max_digits=14, decimal_places=4) + + hh_inc_150_200 = models.DecimalField(max_digits=14, decimal_places=4) + hh_inc_200p = models.DecimalField(max_digits=14, decimal_places=4) + hh_avg_vehicles = models.DecimalField(max_digits=14, decimal_places=4) + hh_avg_size = models.DecimalField(max_digits=14, decimal_places=4) + hh_agg_inc = models.DecimalField(max_digits=14, decimal_places=4) + hh_avg_inc = models.DecimalField(max_digits=14, decimal_places=4) + hh_owner_occ = models.DecimalField(max_digits=14, decimal_places=4) + hh_rental_occ = models.DecimalField(max_digits=14, decimal_places=4) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/future/core_end_state_feature.py b/footprint/main/models/future/core_end_state_feature.py new file mode 100644 index 000000000..cd94f27d1 --- /dev/null +++ b/footprint/main/models/future/core_end_state_feature.py @@ -0,0 +1,107 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0) = models.DecimalField(max_digits=14, decimal_places=4, default=0)) Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation = models.DecimalField(max_digits=14, decimal_places=4, default=0)) version 3 of the License. +# +# This program is distributed in the hope that it will be useful = models.DecimalField(max_digits=14, decimal_places=4, default=0)) but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not = models.DecimalField(max_digits=14, decimal_places=4, default=0)) see . +# +# Contact: Joe DiStefano (joed@calthorpe.com) = models.DecimalField(max_digits=14, decimal_places=4, default=0)) Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201 = models.DecimalField(max_digits=14, decimal_places=4, default=0)) Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com= +from footprint.main.models.geospatial.feature import UpdatingFeature +from footprint.main.models.built_form.built_form import BuiltForm +from django.db import models + +__author__ = 'calthorpe_associates' + + +class CoreEndStateFeature(UpdatingFeature): + built_form = models.ForeignKey(BuiltForm, null=True) + land_development_category = models.CharField(max_length=20, default=None, null=True) + intersection_density_sqmi = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_gross = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_detsf = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_attsf = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_res_mf = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_ret = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_off = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_ind = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_ag = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_emp_military = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_mixed = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_mixed_w_off = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_mixed_no_off = models.DecimalField(max_digits=14, decimal_places=4) + acres_parcel_no_use = models.DecimalField(max_digits=14, decimal_places=4) + + pop = models.DecimalField(max_digits=14, decimal_places=4) + hh = models.DecimalField(max_digits=14, decimal_places=4) + du = models.DecimalField(max_digits=14, decimal_places=4) + du_detsf = models.DecimalField(max_digits=14, decimal_places=4) + du_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4) + du_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4) + du_attsf = models.DecimalField(max_digits=14, decimal_places=4) + du_mf = models.DecimalField(max_digits=14, decimal_places=4) + du_mf2to4 = models.DecimalField(max_digits=14, decimal_places=4) + du_mf5p = models.DecimalField(max_digits=14, decimal_places=4) + + emp = models.DecimalField(max_digits=14, decimal_places=4) + + emp_ret = models.DecimalField(max_digits=14, decimal_places=4) + emp_retail_services = models.DecimalField(max_digits=14, decimal_places=4) + emp_restaurant = models.DecimalField(max_digits=14, decimal_places=4) + emp_accommodation = models.DecimalField(max_digits=14, decimal_places=4) + emp_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4) + emp_other_services = models.DecimalField(max_digits=14, decimal_places=4) + + emp_off = models.DecimalField(max_digits=14, decimal_places=4) + emp_office_services = models.DecimalField(max_digits=14, decimal_places=4) + emp_education = models.DecimalField(max_digits=14, decimal_places=4) + emp_public_admin = models.DecimalField(max_digits=14, decimal_places=4) + emp_medical_services = models.DecimalField(max_digits=14, decimal_places=4) + + emp_ind = models.DecimalField(max_digits=14, decimal_places=4) + emp_wholesale = models.DecimalField(max_digits=14, decimal_places=4) + emp_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4) + emp_manufacturing = models.DecimalField(max_digits=14, decimal_places=4) + emp_construction = models.DecimalField(max_digits=14, decimal_places=4) + emp_utilities = models.DecimalField(max_digits=14, decimal_places=4) + + emp_ag = models.DecimalField(max_digits=14, decimal_places=4) + emp_agriculture = models.DecimalField(max_digits=14, decimal_places=4) + emp_extraction = models.DecimalField(max_digits=14, decimal_places=4) + + emp_military = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_attsf = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_mf = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_retail_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_restaurant = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_accommodation = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_other_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_office_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_public_admin = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_medical_services = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_education = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_wholesale = models.DecimalField(max_digits=14, decimal_places=4) + bldg_sqft_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4) + commercial_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4) + residential_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4) + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/future/core_increment_feature.py b/footprint/main/models/future/core_increment_feature.py new file mode 100644 index 000000000..8dba98bb0 --- /dev/null +++ b/footprint/main/models/future/core_increment_feature.py @@ -0,0 +1,70 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0) Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation version 3 of the License. +# +# This program is distributed in the hope that it will be useful but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not see . +# +# Contact: Joe DiStefano (joed@calthorpe.com) Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201 Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.models.geospatial.feature import Feature, UpdatingFeature + +__author__ = 'calthorpe_associates' + + +class CoreIncrementFeature(UpdatingFeature): + land_development_category = models.CharField(max_length=20, default=None, null=True) + refill_flag = models.IntegerField(null=True) + pop = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_detsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_attsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_mf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp_ret = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_retail_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_restaurant = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_accommodation = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_other_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp_off = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_office_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_education = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_public_admin = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_medical_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp_ind = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_wholesale = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_manufacturing = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_utilities = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_construction = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp_ag = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_agriculture = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_extraction = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp_military = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + + + class Meta(object): + abstract = True + app_label = 'main' diff --git a/footprint/main/models/future/future_scenario_feature.py b/footprint/main/models/future/future_scenario_feature.py new file mode 100644 index 000000000..75c5cb8f9 --- /dev/null +++ b/footprint/main/models/future/future_scenario_feature.py @@ -0,0 +1,120 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.auth.models import User +from django.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.geospatial.feature import PaintingFeature + +__author__ = 'calthorpe_associates' + +class FutureScenarioFeature(PaintingFeature): + """ + A dynamically subclassed abstract class that represents the canvas table for a specific Scenerio. Hence + instances of subclasses of this class correspond to geography rows of the canvas table + """ + objects = GeoInheritanceManager() + total_redev = models.BooleanField(default=False) + + refill_flag = models.BooleanField(default=False) + intersection_density_sqmi= models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_gross = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_developable = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_developing = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res_attsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_res_mf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_ret = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_off = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_ind = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_ag = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_emp_mixed = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_mixed = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_mixed_w_off = models.DecimalField(max_digits=14, decimal_places=4, default=0) + acres_parcel_mixed_no_off = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + pop = models.DecimalField(max_digits=14, decimal_places=4, default=0) + hh = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_attsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_mf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_mf2to4 = models.DecimalField(max_digits=14, decimal_places=4, default=0) + du_mf5p = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_ret = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_retail_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_restaurant = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_accommodation = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_other_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp_off = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_office_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_education = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_public_admin = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_medical_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp_ind = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_wholesale = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_manufacturing = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_construction_utilities = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + emp_ag = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_agriculture = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_extraction = models.DecimalField(max_digits=14, decimal_places=4, default=0) + emp_military = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_detsf_ll = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_detsf_sl = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_attsf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_mf = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_retail_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_restaurant = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_accommodation = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_arts_entertainment = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_other_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_office_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_public_admin = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_medical_services = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_education = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_wholesale = models.DecimalField(max_digits=14, decimal_places=4, default=0) + bldg_sqft_transport_warehousing = models.DecimalField(max_digits=14, decimal_places=4, default=0) + commercial_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4, default=0) + residential_irrigated_sqft = models.DecimalField(max_digits=14, decimal_places=4, default=0) + + @classmethod + def post_save(cls, user_id): + #TODO invalidate cache after painting + from footprint.main.models.analysis_module.core_module.core import Core + core = Core.objects.get(config_entity=cls.config_entity) + # Update the core user to the current user + if not core.user or core.user.id != user_id: + core.user = User.objects.get(id=user_id) + core.save() + core.start() + + class Meta(object): + app_label = 'main' + abstract = True diff --git a/footprint/main/models/geographies/__init__.py b/footprint/main/models/geographies/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/geographies/geography.py b/footprint/main/models/geographies/geography.py new file mode 100644 index 000000000..beb6905a6 --- /dev/null +++ b/footprint/main/models/geographies/geography.py @@ -0,0 +1,39 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models + +__author__ = 'calthorpe_associates' + + +class Geography(models.Model): + """ + Represents a geographic shape such as a parcel, grid cell, line, etc. Other classes having features should + associate to subclasses of this subclass it. + """ + objects = models.GeoManager() + geometry = models.GeometryField() + # An identifier that uniquely identifies the source table that provided these geographies. + source_table_id = models.IntegerField(null=False, db_index=True) + # An identifier that uniquely a row from the source table, usually its id + source_id = models.IntegerField(null=False, db_index=True, max_length=200) + + class Meta(object): + abstract = True, + app_label = 'main' + diff --git a/footprint/main/models/geographies/grid_cell.py b/footprint/main/models/geographies/grid_cell.py new file mode 100644 index 000000000..7bf46cb0d --- /dev/null +++ b/footprint/main/models/geographies/grid_cell.py @@ -0,0 +1,32 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.main.models.geographies.geography import Geography + +__author__ = 'calthorpe_associates' + + +class GridCell(Geography): + """ + Represents an authoritative grid cell definition, whose pk is referenced by other geospatial tables + """ + class Meta(object): + app_label = 'main' + pass + diff --git a/footprint/main/models/geographies/parcel.py b/footprint/main/models/geographies/parcel.py new file mode 100644 index 000000000..dcbe48125 --- /dev/null +++ b/footprint/main/models/geographies/parcel.py @@ -0,0 +1,29 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.geospatial.feature import Feature +__author__ = 'calthorpe_associates' + + +class Parcel(Feature): + """ + Represents an authoritative parcel definition, whose pk is referenced by other geospatial tables + """ + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/geographies/taz.py b/footprint/main/models/geographies/taz.py new file mode 100644 index 000000000..811ca1bdd --- /dev/null +++ b/footprint/main/models/geographies/taz.py @@ -0,0 +1,33 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.main.models.geographies.geography import Geography + +__author__ = 'calthorpe_associates' + + +class Taz(Geography): + """ + Represents an authoritative TAZ (transportation area zone), whose pk is referenced by other geospatial tables + """ + class Meta(object): + app_label = 'main' + + pass + diff --git a/footprint/main/models/geospatial/__init__.py b/footprint/main/models/geospatial/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/geospatial/db_entity.py b/footprint/main/models/geospatial/db_entity.py new file mode 100644 index 000000000..e81efa42e --- /dev/null +++ b/footprint/main/models/geospatial/db_entity.py @@ -0,0 +1,248 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import logging +import re +from django.contrib.auth.models import User +from django.contrib.gis.db import models +from inflection import titleize +from picklefield import PickledObjectField +import sys +from footprint.main.lib.functions import map_dict, accumulate, map_to_dict +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.deletable import Deletable +from footprint.main.mixins.name import Name +from footprint.main.mixins.shared_key import SharedKey +from footprint.main.mixins.tags import Tags +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.main.utils.utils import database_connection_string_for_pys, resolve_module_attr +from footprint.main.utils.uf_toolbox import report_sql_values_as_dict + +__author__ = 'calthorpe_associates' +logger = logging.getLogger(__name__) + +class DbEntity(SharedKey, Name, Tags, Deletable): + """ + Represents a database-derived entity, such as a table, view, or query result. Entities may have tags to help entity_configs that may be interested in them. The relationships between db_entities and entity_configs are registered by the DbEntityInterest class, which also defines the type of interest. Some db_entities are "owned" by one entity_config, while others might simply have an "observe" or "edit" or "dependency" relationship. These roles will be figured out later. The key indicates a specific function of the db_entity, such as "base" to indicate a table used for base data. Tags are broader descriptors, such as "university", used for categorization or queries. DbEntities use the SharedKey mixin to permit multiple DbEntities in the same context (e.g. those of a particular ConfigEntity interest) to share a key. This permits the user to assign multiple tables of the same key (e.g. 'base') to a ConfigEntity instance. The ConfigEntity instance tracks which DbEntity is the default in this situation (see ConfigEntity.selections['db_entities'] and defaults if only one DbEntity with a particular key is present) + """ + objects = GeoInheritanceManager() + + # The optional schema and table or view name from which to base the layer. This will result in a query that selects all + # rows from the table, and the table will be used to configure TileStache or Geoserver. + # These might also identify the query as operating on the specified table and thus be useful for organization + schema = models.CharField(max_length=100, null=True) + table = models.CharField(max_length=100, null=True) + srid = models.CharField(max_length=100, null=True) + + # The origin of this DbEntiy if cloned from a peer + origin_instance = models.ForeignKey('DbEntity', null=True) + + # TODO these should be set to null=False and required + # The user who created the db_entity + creator = models.ForeignKey(User, null=True, related_name='db_entity_creator') + # The user who last updated the db_entity + updater = models.ForeignKey(User, null=True, related_name='db_entity_updater') + + # Describes how to configure the features of the table + feature_class_configuration = PickledObjectField(null=True) + + @property + def full_table_name(self): + """ + Return the schema in table in the syntax usable by Django model classes '"schema"."table"' + :return: + """ + return '"{0}"."{1}"'.format(self.schema, self.table) + + @property + def full_name(self): + """ + Returns the name of the DbEntity with the titleized schema to distinguish it + """ + return "{0} for {1}".format(self.name, titleize(self.schema) if self.schema else 'Global Config') + + def resolve_abstract_feature_class(self): + """ + Extracts the abstract Feature class from the DbEntity configuration + :return The abstract class or None if not configured + """ + abstract_class_path = self.feature_class_config.get('abstract_class', None) + return resolve_module_attr(abstract_class_path) if abstract_class_path else None + + extent_authority = models.BooleanField(default=False) + + # Remote data, such as background layers, use a url instead of schema and table or as an import source + url = models.CharField(max_length=1000, null=True) + # An array of host url prefixes for url based db_entities + hosts = PickledObjectField(null=True) + + # Flag to prevent signal recursion during a save + _no_post_post_save = False + + # TODO replace format with SC token format + # Pickle the Django QuerySet configuration. The manager used is always the manager of the ad hoc class that represents + # the DbEntity via its table argument. There might be a way to have a manager not associated with a table, but that isn't + # yet supported. configuration is an array of queryset items to chain together to form the queryset + # [ + # (values, ['name', 'year']), + # (annotate, dict(average_rating=dict(Avg='book__rating'))), + # (filter, dict(name__like='Bob%')). + # (order_by, ['name', 'year']) + # ] + # means: + # ad_hoc_class.objects.values('name').annotate(average_rating=Avg('book__rating')).order_by('name','year') + # Every tuple has a command and args. The former represents a function such as values, annotate aggregate, order_by + # The latter are either a column/alias path, array of such, or a dictionary for kwargs + # Every kwarg key at the second level is column/alias path and the value is a primitive or dict + # These innermost dicts represent functions where the key is the function name and the value is a primitive or + # column/alias path + # The values snd annotate clause above causes a group_by, which should actually be specified by the same format in group_by + # so that it can be overridden by alternative an group_by or values + query = PickledObjectField(null=True) + + # Holds key that points to dynamic class info for this DbEntity. This key is normally None or the key of another + # DbEntity, the latter if this DbEntity was cloned from another + class_key = models.CharField(max_length=50, null=True) + + @property + def is_clone(self): + return self.class_key and self.class_key != self.key + + + # The same format, but only one dict is expected instead of an array + # If the group_by dict is present, it will be preprended to the query dicts to form the QuerySet definition + group_by = PickledObjectField(null=True) + + def run_query(self, config_entity, **kwargs): + """ + Prepends the given or default group_by (if any) and/or optional values to the query, runs the query, and returns results + TODO Since ordering of query parts makes a big difference, we might have to let kwargs specify positions in the query + :param kwargs['group_by']: use this group_by or the else self.group_by or else no group_by + :param kwargs['values']: use this values to force the query to only return certain fields, + and to return dicts instead of instances. Only pass values if not already defined in the query or group_by field + :return: + """ + + full_query = self._add_to_query(**kwargs) if len(kwargs.keys()) > 0 else None + return self.parse_query(config_entity, full_query) + + def _add_to_query(self, **kwargs): + return kwargs.get('values', []) + kwargs.get('group_by', self.group_by or []) + self.query + + # TODO this is all used only for results and will be replaced by the Sproutcore style stuff in query_parsing + def parse_query(self, config_entity, query=None): + """ + Parses the stored QuerySet components of the DbEntity to create an actual QuerySet + :param query: pass in a query instead of using self.query. Used by run_grouped_query to add the group_by + :return: + """ + query = query or self.query + if not query: + logger.error("Trying to run query for db_entity %s, which has no query defined or passed in" % self.full_name) + return {} + + # Use the base table of the Feature class for now. We'll need to use the rel table soon. + manager = config_entity.feature_class_of_db_entity_key(self.key).__base__.objects + if isinstance(query, basestring): + # String query + # Join the query with the base tables of the feature classes that match the db_entity_keys listed + # as named wildcards in the query string They are in the form %(db_entity_key) + db_entity_keys = re.findall(r'%\((.+?)\)', query) + return report_sql_values_as_dict( + query % map_to_dict( + lambda db_entity_key: [db_entity_key, config_entity.feature_class_of_db_entity_key(db_entity_key).__base__._meta.db_table], + db_entity_keys), + database_connection_string_for_pys(config_entity.db))[0] # assume aggregate + else: + # Combine the query parts + return accumulate(lambda manager, queryset_command: self.parse_and_exec_queryset_command(manager, queryset_command), manager, query) + + def parse_and_exec_queryset_command(self, manager, queryset_command): + """ + A queryset command is a two item tuple of a command and args, the command is e.g. values, annotate, or order_by, and + args are as described in the DbEntity.query docs--a single column/alias path, array of such or kwargs. + :param queryset_command: + :return: + """ + if len(queryset_command) != 2: + raise Exception("Expected queryset_command to be a two item tuple, but got %s".format(queryset_command)) + command, arguments = queryset_command + # Fetch the command from the manager by name and call it with the parsed arguments + return getattr(manager, command)(*self.parse_queryset_command_arguments(arguments)) + + def parse_queryset_command_arguments(self, queryset_command_arguments): + """ + Handles the argument whether a single item, list, or dict, and returns the parsed item(s) in the same + form. For dicts, keys are passed through and the values are parsed + :param queryset_command_arguments: + :return: + """ + return map( + lambda argument: self.parse_queryset_inner_argument(argument), + queryset_command_arguments) + + def parse_queryset_inner_argument(self, argument): + """ + The inner arguments of the queryset command are either a simple column/alias path or a dict in the + case of aggregate functions, e.g. dict(Avg='book__rating') to indicate Avg('book__rating') + :param argument: + :return: + """ + if isinstance(argument, dict): + return map_dict(lambda key, value: self.resolve_models_class(key)(value), argument)[0] + return argument + + def resolve_models_class(self, class_name): + """ + Lookups up classes by string name for aggregate classes like Avg + This could be extended to support other aggregate classes defined elsewhere + :param class_name: unpackaged class name like 'Avg' stored in the query definition + :return: The class + """ + return getattr(sys.modules['django.db.models'], class_name) + + @property + def has_db_url(self): + """ + Indicates whether or not the DbEntity url is configured to point to a postgres database + :return: + """ + return self.url and self.url.startswith('postgres://') + + @property + def has_file_url(self): + """ + Indicates whether or not the DbEntity url is configured to point to a file location + :return: + """ + return self.url and self.url.startswith('file://') + + @property + def importable(self): + """ + Indicates if the DbEntity has the right characteristics for importing feature data. + """ + return self.has_db_url or self.has_file_url or (self.origin_instance and self.origin_instance.importable) + + def __unicode__(self): + return self.name + + class Meta(object): + abstract = False # False to allow multi-table inheritance many-to-many relationship + app_label = 'main' + diff --git a/footprint/main/models/geospatial/db_entity_configuration.py b/footprint/main/models/geospatial/db_entity_configuration.py new file mode 100644 index 000000000..f82c10462 --- /dev/null +++ b/footprint/main/models/geospatial/db_entity_configuration.py @@ -0,0 +1,106 @@ + +from inflection import titleize +from footprint.main.lib.functions import merge, remove_keys +from footprint.main.models.geospatial.db_entity import DbEntity +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.main.utils.utils import resolvable_model_name, full_module_path +from footprint.client.configuration import resolve_fixture, InitFixture + + +__author__ = 'calthorpe_associates' + +def create_db_entity_configuration(config_entity, **kwargs): + """ + Returns a dictionary containing a DbEntity instance and a dynamic feature subclass based on the the class + referenced by kwargs['base_class'] and optionally extra fields referenced by kwargs['fields']. + The dynamic subclass instances represent features of the layer represented by the DbEntity instance. + :param config_entity: Scopes the configuration. If null this will return an "abstract" configuration + :param kwargs: + 'key':Used for the DBEntity key and table name, and also used to name the dynamic subclass of + kwargs['base_class'] TODO: It might be better to use a separate name so that the feature class table + can have the word 'feature' in it instead of 'layer; + 'name':Optional name for the DBEntity. By default the key value is used + 'db_entity_class' is optional and indicates the subclass to construct rather than DbEntity + 'base_class': the class to subclass whose instances represent features of the DbEntity. + 'fields': array of additional model fields for the subclass + :return: a dict with a 'db_entity' key pointing to the DbEntity instance and a 'feature_class' key pointing to + the dynamic feature subclass that was created based on the base_class and config_entity + """ + + if not config_entity: + return abstract_db_entity_configuration(**kwargs) + name = '{0}'.format( + # The name is passed in named or the titlized version of key + kwargs.get('name', None) or titleize(kwargs['key'])) + db_entity_class = kwargs.get('db_entity_class', DbEntity) + db_entity_class_name = resolvable_model_name(db_entity_class) + + schema = config_entity.schema() + init_fixture = resolve_fixture(None, "init", InitFixture, schema) + connection = init_fixture.import_database() + # Unless overridden, create the url according to this postgres url scheme + url = kwargs.get('url', + 'postgres://{user}:{password}/{host}:{port}/{database}'.format( + **merge(dict(port=5432), + connection)) if connection else None) + + # We distinguish the DbEntity by key, name, and schema. Multiple DbEntities with the same key is a schema + # may exist, but they must have different names to distinguish them + feature_class_creator = FeatureClassCreator(config_entity) + return dict( + # This is used to determine what DbEntity subclass to create, if any + db_entity_class_name=db_entity_class_name, + key=kwargs['key'], + schema=schema, + name=name, + url=url, + table=kwargs.get('table', kwargs['key'] if not kwargs.get('query', None) else None), + query=kwargs.get('query', None), + group_by=kwargs.get('group_by', None), + class_key=kwargs.get('class_key', None), + hosts=kwargs.get('hosts', None), # TODO used? + extent_authority=kwargs.get('extent_authority', False), + creator=kwargs.get('creator', None), + srid=kwargs.get('srid', None), + feature_class_configuration= + # If a feature_class_configuration is specified explicitly, that means we are cloning the db_entity from another config_entity + # So just copy it and update for this config_entity + feature_class_creator.feature_class_configuration_for_config_entity(kwargs['feature_class_configuration']) if \ + kwargs.get('feature_class_configuration') else \ + # Formulate the feature_class_configuration from the db_entity_configuratoin + feature_class_creator.get_feature_class_configuration( + **remove_keys(kwargs, ['query', 'group_by', 'class_key', 'hosts', 'extent_authority', 'creator', 'srid']) + ) if not 'no_feature_class_configuration' in kwargs else None + ) + +def abstract_db_entity_configuration(**kwargs): + """ + Returns a minimized db_entity_configuration when no config_entity is in scope. This is primarily to lookup the abstract + Feature class by db_entity_key + :param kwargs: + :return: + """ + return dict( + key=kwargs['key'], + feature_class_configuration=dict( + abstract_class=full_module_path(kwargs['base_class']) + ) + ) + +def db_entity_configuration_keys(): + # The keys of the DbEntity and DbEntity configuration, used for cloning + return [ + 'db_entity_class_name', + 'key', + 'schema', + 'name', + 'url', + 'table', + 'query', + 'group_by', + 'class_key', + 'url', + 'hosts', + 'extent_authority', + 'feature_class_configuration', + ] diff --git a/footprint/main/models/geospatial/feature.py b/footprint/main/models/geospatial/feature.py new file mode 100644 index 000000000..8780224e8 --- /dev/null +++ b/footprint/main/models/geospatial/feature.py @@ -0,0 +1,103 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from django.contrib.gis.db.models import GeoManager +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.timestamps import Timestamps + +__author__ = 'calthorpe_associates' + +class Feature(models.Model): + """ + A mixin model class that references a Geography instance. The abstract Geography class and its subclasses + represent geography authorities, so that any shared geographies like regional parcels have one authoritive + locations. Derived classes and their instances, such as the ConfigEntityGeography derivative classes use the + Geographic mixin to reference the authoritative geographies of the Geography class hierarchy + """ + + # Base manager inherited by subclasses + objects = GeoManager() + + # The geometry imported for the feature + wkb_geometry = models.GeometryField() + + def __getattribute__(self, name): + """ + Override that, if an attribute isn't found on the object, then it instead + looks for the same attribute prefixed with 'temp_' and tries to return + that value. + """ + + try: + return object.__getattribute__(self, name) + except AttributeError: + if '_id' in name: + temp_name = name.replace('_id', '') + return object.__getattribute__(self, temp_name)[0].id + else: + temp_name = '{0}s'.format(name) + return object.__getattribute__(self, temp_name)[0] + + @classmethod + def post_save(cls, user_id): + """ + Optional class method to kick of analytic modules (see FutureScenarioFeature) + """ + pass + + + class Meta(object): + abstract = True + app_label='main' + +class UpdatingFeature(Feature, Timestamps): + objects = GeoManager() + + class Meta(object): + abstract = True + app_label='main' + + +class PaintingFeature(Feature, Timestamps): + objects = GeoManager() + dev_pct = models.DecimalField(max_digits=8, decimal_places=4, default=1.0000) + density_pct = models.DecimalField(max_digits=8, decimal_places=4, default=1.0000) + dirty_flag = models.BooleanField(default=False) + + class Meta(object): + abstract = True + app_label='main' + + + +class FeatureGeography(models.Model): + """ + An abstract class representing the association between a Feature class and Geography class + """ + objects = GeoInheritanceManager() + + # Associates a Feature class to a Geography class + # Assign these to ForeignKey fields + feature = None + geography = None + + def __unicode__(self): + return "FeatureGeography:Feature:{1}, Geography:{2}".format(self.feature, self.geography) + class Meta(object): + app_label = 'main' + abstract = True diff --git a/footprint/main/models/geospatial/feature_class_creator.py b/footprint/main/models/geospatial/feature_class_creator.py new file mode 100644 index 000000000..996b3bf1e --- /dev/null +++ b/footprint/main/models/geospatial/feature_class_creator.py @@ -0,0 +1,387 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. + +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import logging +from django.contrib.gis.db import models +from django.db.models.fields import Field +from footprint.main.lib.functions import map_dict_to_dict, map_to_dict, merge, remove_keys, unique, get_single_value_or_none, flat_map, filter_keys, filter_dict +from footprint.main.models.geospatial.feature import Feature +from footprint.main.utils.dynamic_subclassing import get_dynamic_model_class_name, dynamic_model_class +from footprint.main.utils.inline_inspectdb import InlineInspectDb +from footprint.main.utils.utils import resolve_module_attr, get_property_path, full_module_path, resolve_model +from footprint.main.models.config.config_entity import ConfigEntity + +logger = logging.getLogger(__name__) + +class FeatureClassCreator(object): + def __init__(self, config_entity, db_entity=None): + """ + Creates a FeatureClassCreator for the given ConfigEntity, and optionally specific to a DbEntity of the ConfigEntity + """ + + self.config_entity = config_entity.subclassed_config_entity + self.db_entity = db_entity + + def db_entity_to_feature_class_lookup(self): + """ + Returns the db_entity to feature_classes of the config_entity.computed_db_entities() + :return: + """ + return map_to_dict(lambda db_entity: [db_entity, FeatureClassCreator(self.config_entity, db_entity).dynamic_feature_class()], + filter(lambda db_entity: db_entity.feature_class_configuration, self.config_entity.computed_db_entities())) + + @staticmethod + def db_entity_key_to_feature_class_lookup(config_entity, db_entity_configurations=None): + """ + Returns all db_entity_configuration keys mapped to a dynamic Feature subclass or None if no Feature + class is configured for the db_entity_configuration + :param config_entity: Used to scope the Feature classes. If no then abstract classes are returned + :param db_entity_configurations: Optional specific set of configurations. If omitted, the db_entity.computed_db_enties() + will be used. You must specify this before the db_entities have been created on the config_entity, for the case + where the config_entity is null. + :return: A dict keyed by db_entity key and valued by a dynamic Feature subclass or None + """ + if not config_entity: + return FeatureClassCreator.db_entity_key_to_abstract_feature_class_lookup(db_entity_configurations) + + # Get the config_entity from the first self.db_entity_configuration with a feature_class_configuration + # Get the corresponding db_entities from the config_entity + db_entities = map( + lambda db_entity_configuration: config_entity.computed_db_entities(key=db_entity_configuration['key'])[0], + db_entity_configurations) + return map_to_dict(lambda db_entity: [db_entity.key, FeatureClassCreator(config_entity, db_entity).dynamic_feature_class()], + db_entities) + + @staticmethod + def db_entity_key_to_abstract_feature_class_lookup(db_entity_configurations): + """ + Like self.db_entity_key_to_feature_class_lookup, but used when no ConfigEntity is in scope. + This returns the abstract version of the Feature subclasses by self.db_entity_key + :param db_entity_configurations: + :return: + """ + return map_to_dict(lambda db_entity_configuration: [db_entity_configuration['key'], + resolve_module_attr(get_property_path(db_entity_configuration, 'feature_class_configuration.abstract_class'))], + db_entity_configurations) + + def ensure_dynamic_models(self): + """ + For a given run of the application, make sure that all the dynamic model classes of the config_entity have been created. + We only want to create model classes once per application run, and once they are created below we shouldn't have to check + to see if they exist. If need models are created by layer import, etc, that process is responsible for creating the classes, + and then they will be created here on the subsequent application runs + """ + if self.config_entity._dynamic_models_created: + return + + # Create all feature classes, both the base version and the rel version + # TODO the filter_dict is here to filter out null values due to DbEntity corruption. This should go away as things stablize + db_entity_to_feature_class = filter_dict(lambda key, value: value, self.db_entity_to_feature_class_lookup()) + # Ensure that the dynamic geography class of each uniquely represented self.db_entity-owning config_entity + for db_entity in unique(db_entity_to_feature_class.keys(), lambda db_entity: db_entity.feature_class_configuration['geography_scope']): + FeatureClassCreator(self.config_entity, db_entity).dynamic_geography_class() + + def dynamic_geography_class_name(self): + return get_dynamic_model_class_name(resolve_module_attr('footprint.main.models.geographies.geography.Geography'), + self.db_entity.feature_class_configuration['geography_scope']) + + def dynamic_geography_class(self): + """ + Return the geography class based on the db_entity or config_entity + """ + scope = ConfigEntity._subclassed_config_entity_by_id(self.db_entity.feature_class_configuration['geography_scope'] if self.db_entity else self.config_entity) + return dynamic_model_class( + resolve_module_attr('footprint.main.models.geographies.geography.Geography'), + scope.schema(), + 'geography', + self.dynamic_geography_class_name(), + scope=scope + ) + + @property + def feature_class_is_ready(self): + """ + Returns True is enough configuration exists to create the dynamic feature class. + """ + feature_class_configuration = self.db_entity.feature_class_configuration + # This is a bit of a hack, but it's assumed the feature_class_configuration is ready when it has + # an abstract_class, since this is the way that imported feature classes work. + return feature_class_configuration and feature_class_configuration.get('abstract_class', False) + + def dynamic_feature_class(self, base_only=False): + """ + Gets or creates a DbEntity Feature subclass based on the given configuration. + There are two classes get or created. The base models the imported table. + The child subclasses the base and adds relations. This way the imported table is not manipulated. + The child class is returned unless base_only=True is specified + :params base_only: Default False, indicates that only the base feature class is desired, not the subclass + that contains the relationships + :return: The dynamic subclass of the subclass given in feature_class_configuration or None + """ + feature_class_configuration = self.db_entity.feature_class_configuration + if not feature_class_configuration: + return None + + # Create the base class to represent the "raw" table + try: + abstract_feature_class = resolve_module_attr(feature_class_configuration['abstract_class']) + except Exception, e: + # TODO. This shouldn't ever happen once DbEntity/Layer saving is stable + logger.error("Corrupt DbEntity %s." % self.db_entity.name) + return + + existing_field_names = map(lambda field: field.name, + filter(lambda field: isinstance(field, Field), abstract_feature_class._meta.fields)) + fields = filter(lambda field: isinstance(field, Field) and field.name not in existing_field_names+['id'], feature_class_configuration.get('fields', [])) + + base_feature_class = dynamic_model_class( + abstract_feature_class, + feature_class_configuration['schema'], + feature_class_configuration['table'], + class_name="{0}{1}".format(abstract_feature_class.__name__, self.db_entity.id), + fields=map_to_dict(lambda field: [field.name, field], fields), + # (no extra fields defined here in the parent) + class_attrs=feature_class_configuration.get('class_attrs', {}), + related_class_lookup=feature_class_configuration.get('related_class_lookup', {}) + ) + + if base_only: + # If the child class isn't needed, return the base + return base_feature_class + + # Create the child class that subclasses the base and has the related fields + return dynamic_model_class( + base_feature_class, + feature_class_configuration['schema'], + '{0}rel'.format(feature_class_configuration['table']), + class_name="{0}{1}Rel".format(abstract_feature_class.__name__, self.db_entity.id), + fields=merge( + # Create all related fields. These are ForeignKey fields for single values and ManyToMany for many values + self.create_related_fields(), + # Create the ManyToMany geographies association that associates the feature to the primary geographies that it intersects. + # Even if this feature contains primary geographies remains a many property in case there are multiple primary geography tables + dict(geographies=models.ManyToManyField(self.dynamic_geography_class_name(), + db_table='"{schema}"."{table}_geography"'.format( + schema=feature_class_configuration['schema'], + table=feature_class_configuration['table']))) + ), + class_attrs=feature_class_configuration.get('class_attrs', {}), + related_class_lookup=feature_class_configuration.get('related_class_lookup', {}) + ) + + def create_related_fields(self): + """ + Create ForeignKey and ManyFields for each db_entity.feature_class_configuration.related_fields + :return: + """ + return map_dict_to_dict( + lambda field_name, related_field_configuration: self.create_related_field( + field_name, + related_field_configuration), + self.db_entity.feature_class_configuration.get('related_fields', {})) + + def related_descriptors(self): + """ + Returns the existing related field descriptors that were created by create_related_fields and assigned + to the dynamic feature class + :return: A dict keyed by field name, valued by the ManyToMany field or equivalent + """ + + feature_class = self.config_entity.feature_class_of_db_entity_key(self.db_entity.key) + return map_dict_to_dict( + lambda field_name, related_field_configuration: [field_name, getattr(feature_class, field_name)], + self.db_entity.feature_class_configuration.get('related_fields', {})) + + + def feature_class_configuration_from_introspection(self): + """ + Creates a dynamic Feature class configuration by introspecting the db_entity's Feature table. + :return: The Feature class configuration + """ + fields = InlineInspectDb.get_fields(self.db_entity.full_table_name) + return self.create_feature_class_configuration(fields.values()) + + def feature_class_configuration_from_geojson_introspection(self, data): + """ + Creates a dynamic Feature class configuration by introspecting the db_entity's Feature table. + :return: The Feature class configuration + """ + properties = unique(flat_map(lambda feature: feature.properties.keys(), data.features)) + fields = map(lambda property: models.CharField(property), properties) + return self.create_feature_class_configuration(fields) + + FEATURE_CLASS_CONFIGURATION_KEYS = [ + 'abstract_class', + 'schema', + 'table', + 'class_name', + 'class_attrs', + 'related_class_lookup', + 'scope', + 'geography_scope', + 'db_entity_key', + 'fields', + 'source_id_column', + 'source_from_origin_layer_selection', + 'intersection', + 'generated', + 'data_importer' + ] + @classmethod + def feature_class_configuration_keys(cls): + """ + These are all the possible keys in the feature_class_configuration. + This is used for cloning + """ + return cls.FEATURE_CLASS_CONFIGURATION_KEYS + + def resolve_geography_scope(self): + """ + The config_entity id ancestor whose geography table is used by this ConfigEntity depends on its class. + If it is a Scenario it uses that of the Project, otherwise it uses the one in its own schema. + The reason for this is that Scenario feature tables needed to be joined + to Project primary geographies for querying. Really the way this is should work is that geography association tables + are created at the Project and Region scopes, but for now we just do one. + """ + return self.config_entity.project.id if isinstance(self.config_entity, resolve_model('main.Scenario')) else self.config_entity.id + + def create_feature_class_configuration(self, fields=[]): + """ + Creates a feature_class_configuration for db_entities not pre-configured, in other words, those that + were uploaded. + """ + + # Use the pk for the source_id_column + primary_key_fields = filter(lambda field: field.primary_key, fields) + # Use the existing primary key or default to the default Django one + # The former case applies to imported tables that have primary keys, the latter to tables that are + # created by Django + source_id_column = primary_key_fields[0].name if primary_key_fields else 'id' + + return dict( + abstract_class=full_module_path(Feature), + schema=self.config_entity.schema(), + table=self.db_entity.key, + class_name=None, + class_attrs={'config_entity__id': self.config_entity.id, 'override_db': self.config_entity.db, 'db_entity_key': self.db_entity.key}, + related_class_lookup=dict(config_entity='footprint.main.models.config.config_entity.ConfigEntity'), + scope=self.config_entity.id, + # The scope of the Geography table. Scenarios always scope to the Project geography table, for now + geography_scope=self.resolve_geography_scope(), + db_entity_key=self.db_entity.key, + fields=fields, + source_id_column=source_id_column, + # Indicates that the features should be created from the db_entity's LayerSelection features. + # The LayerSelection is based on the user who last updated the db_entity (db_entity.updater) + source_from_origin_layer_selection=False, + # Default to centroid-centroid intersection. + # TODO this should be specified during the import process + intersection=dict(type='polygon'), + # Generated tells us that the feature_class_configuration wasn't pre-configured in code + generated=True, + # This will be set to a custom importer class path, such as the ShapeFileProcessor, + # depending on how the feature data is imported. + data_importer=None + ) + + def get_feature_class_configuration(self, **kwargs): + """ + Creates a full Feature_class_configuration based on the kwargs + """ + + from footprint.main.publishing import data_import_publishing + return dict( + abstract_class=full_module_path(kwargs.get('base_class', Feature)), + schema=self.config_entity.schema(), + table=kwargs['key'], + class_name=None, + class_attrs={'config_entity__id': self.config_entity.id, 'override_db': self.config_entity.db, 'db_entity_key': kwargs['key']}, + related_class_lookup=dict(config_entity='footprint.main.models.config.config_entity.ConfigEntity'), + scope=self.config_entity.id, + # The scope of the Geography table. Scenarios always scope to the Project geography table, for now + geography_scope=self.resolve_geography_scope(), + db_entity_key=kwargs['key'], + # Indicates that the configuration was generated by introspecting a table, rather than by deliberate configuration + generated=False, + # Indicates that the features should be created from the db_entity's LayerSelection features. + # The LayerSelection is based on the user who last updated the db_entity (db_entity.updater) + source_from_origin_layer_selection=False, + # The default data_importer for features + # The default imports based on the url of the db_entity--usually a special database url + data_importer=full_module_path(data_import_publishing.DefaultImportProcessor), + # Pass the remaining keys straight through from the db_entity configuration + **remove_keys(kwargs, ['key', 'base_class']) + ) + + def feature_class_configuration_for_config_entity(self, feature_class_configuration): + """ + Used when cloning the db_entity of a config_entity. This takes the feature_class_configuration + of the source config_entity and updates it scope to that of this config_entity + """ + return merge(feature_class_configuration, dict( + scope=self.config_entity.id, + # The scope of the Geography table. Scenarios always scope to the Project geography table, for now + geography_scope=self.resolve_geography_scope(), + schema=self.config_entity.schema() + )) + + def create_related_field(self, field_name, related_field_configuration): + """ + Creates a ForeignKey or ManyToMany field based on related_field_configuration + :param field_name: + :param related_field_configuration: A dict containing related_db_entity_key or related_class_name + related_db_entity_key: the db_entity_key of the config_entity whose feature_class is the relation type + related_class_name: any model class, such as BuiltForm, to relate to. + single: if True this is a ForeignKey (toOne) relationship. Otherwise a ManyToMany is created + :return: A tuple. First item is the field name and the value is the field. + """ + + config_entity = ConfigEntity._subclassed_config_entity_by_id(self.db_entity.feature_class_configuration['scope']) + if related_field_configuration.get('related_db_entity_key', None): + # field name matches name of peer self.db_entity_key--get it's feature class name + related_db_entity = get_single_value_or_none(config_entity.computed_db_entities(key=related_field_configuration['related_db_entity_key'])) + related_class_name_or_model = FeatureClassCreator(self.config_entity, related_db_entity).dynamic_feature_class() + elif related_field_configuration.get('related_class_name', None): + # A model class such as BuiltForm + related_class_name_or_model = resolve_module_attr(related_field_configuration['related_class_name']) + else: + raise Exception("No related_db_entity_key or related_class_name found on feature_class_configuration for self.db_entity %s" % self.db_entity.key) + + if related_field_configuration.get('single', None): + return [field_name, + models.ForeignKey(related_class_name_or_model, null=True)] + else: + return [field_name, + models.ManyToManyField(related_class_name_or_model, + # Pass a custom, readable table name for the through class for ManyToMany relations + db_table='"{schema}"."{table}_{field_name}"'.format( + schema=self.db_entity.feature_class_configuration['schema'], + table=self.db_entity.feature_class_configuration['table'], + field_name=field_name))] + + def merge_feature_class_configuration_into_db_entity(self, feature_class_configuration): + # Update our DbEntity so we have the configuration available for future operations + # Note that we always want to generate the feature_class_configuration here if its generated in case + # the table schema is updated and we need to modify the configuration + self.db_entity.feature_class_configuration = merge( + self.db_entity.feature_class_configuration, + feature_class_configuration) + # Disable signals and update the DbEntity + self.db_entity._no_post_post_save = True + self.db_entity.save() + self.db_entity._no_post_post_save = False diff --git a/footprint/main/models/keys/__init__.py b/footprint/main/models/keys/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/keys/built_form_keys.py b/footprint/main/models/keys/built_form_keys.py new file mode 100644 index 000000000..c68a1776a --- /dev/null +++ b/footprint/main/models/keys/built_form_keys.py @@ -0,0 +1,262 @@ +__author__ = 'calthorpe_associates' + + +class BuiltFormKeys(object): + """ + Keys for used by Built Forms + """ + + BUILDING_USE_RESIDENTIAL = 'Residential' + BUILDING_USE_OFFICE = 'Office' + BUILDING_USE_RETAIL = 'Retail' + BUILDING_USE_INDUSTRIAL = 'Industrial' + + INFRASTRUCTURE_STREET = PLACETYPE_COMPONENT_STREET = 'Street' + INFRASTRUCTURE_UTILITIES = PLACETYPE_COMPONENT_UTILITY = 'Utility' + INFRASTRUCTURE_PARK = PLACETYPE_COMPONENT_PARK = 'Park' + INFRASTRUCTURE_TYPES = [INFRASTRUCTURE_STREET, INFRASTRUCTURE_UTILITIES, INFRASTRUCTURE_PARK] + + BUILDINGTYPE_CIVIC = 'Civic' + BUILDINGTYPE_RESIDENTIAL = 'Residential' + BUILDINGTYPE_DETACHED_RESIDENTIAL = 'Detached Residential' + BUILDINGTYPE_ATTACHED_AND_MULTIFAMILY = 'Attached and Multifamily Residential' + BUILDINGTYPE_OFFICE_INDUSTRIAL = 'Office/Industrial' + BUILDINGTYPE_COMMERCIAL_RETAIL = 'Commercial/Retail' + BUILDINGTYPE_MIXED_USE = 'Mixed Use' + BUILDINGTYPE_INSTITUTIONAL = 'Institutional' + BUILDINGTYPE_BLANK = 'Blank' + BUILDINGTYPE_AGRICULTURAL = 'Agricultural' + + RESIDENTIAL_BUILDINGTYPE_CATEGORIES = [ + BUILDINGTYPE_RESIDENTIAL, BUILDINGTYPE_DETACHED_RESIDENTIAL, + BUILDINGTYPE_ATTACHED_AND_MULTIFAMILY + ] + + EMPLOYMENT_BUILDINGTYPE_CATEGORIES = [ + BUILDINGTYPE_AGRICULTURAL, + BUILDINGTYPE_OFFICE_INDUSTRIAL, BUILDINGTYPE_COMMERCIAL_RETAIL, BUILDINGTYPE_INSTITUTIONAL + ] + + NET_COMPONENTS = [BUILDINGTYPE_RESIDENTIAL, + BUILDINGTYPE_DETACHED_RESIDENTIAL, + BUILDINGTYPE_ATTACHED_AND_MULTIFAMILY, + BUILDINGTYPE_OFFICE_INDUSTRIAL, + BUILDINGTYPE_COMMERCIAL_RETAIL, + BUILDINGTYPE_MIXED_USE, + BUILDINGTYPE_INSTITUTIONAL, + BUILDINGTYPE_BLANK, + BUILDINGTYPE_AGRICULTURAL] + + GROSS_COMPONENTS = INFRASTRUCTURE_TYPES + [BUILDINGTYPE_CIVIC] + + COMPONENT_CATEGORIES = NET_COMPONENTS + GROSS_COMPONENTS + + ATTACHED_RESIDENTIAL = 'Attached Single Family' + MULTIFAMILY_2_TO_4 = 'Multifamily 2 To 4' + MULTIFAMILY_5P = 'Multifamily 5 Plus' + LARGE_LOT_SINGLE_FAMILY = 'Single Family Large Lot' + SMALL_LOT_SINGLE_FAMILY = 'Single Family Small Lot' + + MULTIFAMILY_AND_ATTACHED = [ATTACHED_RESIDENTIAL, MULTIFAMILY_2_TO_4, MULTIFAMILY_5P] + DETACHED_RESIDENTIAL = [LARGE_LOT_SINGLE_FAMILY, SMALL_LOT_SINGLE_FAMILY] + + RESIDENTIAL_CATEGORY = 'Residential' + RETAIL_CATEGORY = 'Retail' + OFFICE_CATEGORY = 'Office' + INDUSTRIAL_CATEGORY = 'Industrial' + AGRICULTURAL_CATEGORY = 'Agricultural' + MILITARY_CATEGORY = 'Armed Forces' + + RESIDENTIAL_SUBCATEGORIES = MULTIFAMILY_AND_ATTACHED + DETACHED_RESIDENTIAL + ALL_RESIDENTIAL_USES = MULTIFAMILY_AND_ATTACHED + DETACHED_RESIDENTIAL + [RESIDENTIAL_CATEGORY] + + TOP_LEVEL_EMPLOYMENT_CATEGORIES = [RETAIL_CATEGORY, + OFFICE_CATEGORY, + INDUSTRIAL_CATEGORY, + AGRICULTURAL_CATEGORY, + MILITARY_CATEGORY] + + RETAIL_SUBCATEGORIES = ['Retail Services', 'Restaurant', 'Accommodation', 'Arts Entertainment', 'Other Services'] + OFFICE_SUBCATEGORIES = ['Office Services', 'Public Admin', 'Education Services', 'Medical Services'] + INDUSTRIAL_SUBCATEGORIES = ['Manufacturing', 'Wholesale', 'Transport Warehouse', 'Construction Utilities'] + AGRICULTURAL_SUBCATEGORIES = ['Agriculture', 'Extraction'] + + BUILDING_USE_DEFINITION_METACATEGORIES = [ + RESIDENTIAL_CATEGORY, + OFFICE_CATEGORY, + RETAIL_CATEGORY, + INDUSTRIAL_CATEGORY, + AGRICULTURAL_CATEGORY + ] + + BUILDING_USE_DEFINITION_CATEGORIES = { + 'Retail Services': RETAIL_CATEGORY, + 'Restaurant': RETAIL_CATEGORY, + 'Accommodation': RETAIL_CATEGORY, + 'Arts Entertainment': RETAIL_CATEGORY, + 'Other Services': RETAIL_CATEGORY, + + 'Office Services': OFFICE_CATEGORY, + 'Public Admin': OFFICE_CATEGORY, + 'Education Services': OFFICE_CATEGORY, + 'Medical Services': OFFICE_CATEGORY, + + 'Manufacturing': INDUSTRIAL_CATEGORY, + 'Wholesale': INDUSTRIAL_CATEGORY, + 'Transport Warehouse': INDUSTRIAL_CATEGORY, + 'Construction Utilities': INDUSTRIAL_CATEGORY, + + 'Agriculture': AGRICULTURAL_CATEGORY, + 'Extraction': AGRICULTURAL_CATEGORY, + + 'Armed Forces': MILITARY_CATEGORY, + + LARGE_LOT_SINGLE_FAMILY: RESIDENTIAL_CATEGORY, + SMALL_LOT_SINGLE_FAMILY: RESIDENTIAL_CATEGORY, + ATTACHED_RESIDENTIAL: RESIDENTIAL_CATEGORY, + MULTIFAMILY_2_TO_4: RESIDENTIAL_CATEGORY, + MULTIFAMILY_5P: RESIDENTIAL_CATEGORY, + } + BUILDINGTYPE_SOURCE_ID_LOOKUP = { + 'by_name': { + 'Airport': {'fp_id': None, 'source_id': 62}, + 'Campus/College High': {'fp_id': 395, 'source_id': 43}, + 'Campus/College Low': {'fp_id': 346, 'source_id': 44}, + 'Church': {'fp_id': 394, 'source_id': 59}, + 'Connected Tourism': {'fp_id': None, 'source_id': 64}, + 'Estate Lot': {'fp_id': 352, 'source_id': 21}, + 'Garden Apartment': {'fp_id': 373, 'source_id': 16}, + 'Health Office': {'fp_id': None, 'source_id': 66}, + 'High-Rise Mixed Use': {'fp_id': 344, 'source_id': 2}, + 'High-Rise Office': {'fp_id': 351, 'source_id': 25}, + 'High-Rise Residential': {'fp_id': 380, 'source_id': 9}, + 'Hospital/Civic/Other Institutional': {'fp_id': 400, 'source_id': 45}, + 'Hotel High': {'fp_id': 358, 'source_id': 37}, + 'Hotel Low': {'fp_id': 378, 'source_id': 38}, + 'Industrial High': {'fp_id': 385, 'source_id': 33}, + 'Industrial Low': {'fp_id': 347, 'source_id': 34}, + 'Large Lot 7500': {'fp_id': 399, 'source_id': 20}, + 'Low Density Commercial': {'fp_id': None, 'source_id': 61}, + 'Low Intensity Strip Commercial (weighted avg)': {'fp_id': 342, 'source_id': 41}, + 'Low-Rise Mixed Use': {'fp_id': 360, 'source_id': 4}, + 'Low-Rise Office': {'fp_id': 361, 'source_id': 27}, + 'Main Street Commercial (Retail + Office/Medical)': {'fp_id': 396, 'source_id': 28}, + 'Main Street Commercial/MU High (3-5 Floors)': {'fp_id': 392, 'source_id': 6}, + 'Main Street Commercial/MU Low (1-2 Floors)': {'fp_id': 343, 'source_id': 7}, + 'Medium Intensity Strip Commercial (weighted avg)': {'fp_id': 365, 'source_id': 40}, + 'Medium Lot 5500': {'fp_id': 353, 'source_id': 19}, + 'Mid-Rise Mixed Use': {'fp_id': 357, 'source_id': 3}, + 'Mid-Rise Office': {'fp_id': 363, 'source_id': 26}, + 'Military General Catch-All': {'fp_id': None, 'source_id': 60}, + 'Non-Urban Elementary School': {'fp_id': 387, 'source_id': 47}, + 'Non-Urban High School': {'fp_id': 348, 'source_id': 51}, + 'Non-Urban Middle School': {'fp_id': 345, 'source_id': 49}, + 'Office Park High': {'fp_id': 390, 'source_id': 31}, + 'Office Park Low': {'fp_id': 377, 'source_id': 32}, + 'Open Space': {'fp_id': None, 'source_id': 65}, + 'Park/Recreation': {'fp_id': None, 'source_id': 63}, + 'Parking Structure': {'fp_id': 370, 'source_id': 30}, + 'Parking Structure+Ground-Floor Retail': {'fp_id': 397, 'source_id': 29}, + 'Parking Structure/Mixed Use': {'fp_id': 359, 'source_id': 5}, + 'Public Infrastructure': {'fp_id': None, 'source_id': 68}, + 'Regional Mall': {'fp_id': 391, 'source_id': 39}, + 'Rural Employment': {'fp_id': 389, 'source_id': 42}, + 'Rural Ranchette': {'fp_id': 369, 'source_id': 23}, + 'Rural Residential': {'fp_id': 382, 'source_id': 22}, + 'Skyscraper Mixed Use': {'fp_id': 384, 'source_id': 1}, + 'Skyscraper Office': {'fp_id': 362, 'source_id': 24}, + 'Skyscraper Residential': {'fp_id': 355, 'source_id': 8}, + 'Small Lot 4000': {'fp_id': 354, 'source_id': 18}, + 'Standard Podium Multi-Family': {'fp_id': 366, 'source_id': 12}, + 'Standard Townhome': {'fp_id': 388, 'source_id': 15}, + 'Suburban Civic Complex': {'fp_id': 368, 'source_id': 57}, + 'Suburban Multifamily Apt/Condo': {'fp_id': 381, 'source_id': 13}, + 'Town Civic Complex': {'fp_id': 372, 'source_id': 56}, + 'Town/Branch Library': {'fp_id': 367, 'source_id': 58}, + 'Transit/Rail Station': {'fp_id': None, 'source_id': 67}, + 'Urban City Hall': {'fp_id': 386, 'source_id': 52}, + 'Urban Convention Center': {'fp_id': 374, 'source_id': 55}, + 'Urban Courthouse': {'fp_id': 393, 'source_id': 54}, + 'Urban Elementary School': {'fp_id': 383, 'source_id': 46}, + 'Urban High School': {'fp_id': 356, 'source_id': 50}, + 'Urban Mid-Rise Residential': {'fp_id': 349, 'source_id': 10}, + 'Urban Middle School': {'fp_id': 350, 'source_id': 48}, + 'Urban Podium Multi-Family': {'fp_id': 371, 'source_id': 11}, + 'Urban Public Library': {'fp_id': 375, 'source_id': 53}, + 'Urban Townhome/Live-Work': {'fp_id': 379, 'source_id': 14}, + 'Very Small Lot 3000': {'fp_id': 376, 'source_id': 17}, + 'Warehouse High': {'fp_id': 364, 'source_id': 35}, + 'Warehouse Low': {'fp_id': 398, 'source_id': 36} + }, + + 'by_source_id': { + 1: 'Skyscraper Mixed Use', + 2: 'High-Rise Mixed Use', + 3: 'Mid-Rise Mixed Use', + 4: 'Low-Rise Mixed Use', + 5: 'Parking Structure/Mixed Use', + 6: 'Main Street Commercial/MU High (3-5 Floors)', + 7: 'Main Street Commercial/MU Low (1-2 Floors)', + 8: 'Skyscraper Residential', + 9: 'High-Rise Residential', + 10: 'Urban Mid-Rise Residential', + 11: 'Urban Podium Multi-Family', + 12: 'Standard Podium Multi-Family', + 13: 'Suburban Multifamily Apt/Condo', + 14: 'Urban Townhome/Live-Work', + 15: 'Standard Townhome', + 16: 'Garden Apartment', + 17: 'Very Small Lot 3000', + 18: 'Small Lot 4000', + 19: 'Medium Lot 5500', + 20: 'Large Lot 7500', + 21: 'Estate Lot', + 22: 'Rural Residential', + 23: 'Rural Ranchette', + 24: 'Skyscraper Office', + 25: 'High-Rise Office', + 26: 'Mid-Rise Office', + 27: 'Low-Rise Office', + 28: 'Main Street Commercial (Retail + Office/Medical)', + 29: 'Parking Structure+Ground-Floor Retail', + 30: 'Parking Structure', + 31: 'Office Park High', + 32: 'Office Park Low', + 33: 'Industrial High', + 34: 'Industrial Low', + 35: 'Warehouse High', + 36: 'Warehouse Low', + 37: 'Hotel High', + 38: 'Hotel Low', + 39: 'Regional Mall', + 40: 'Medium Intensity Strip Commercial (weighted avg)', + 41: 'Low Intensity Strip Commercial (weighted avg)', + 42: 'Rural Employment', + 43: 'Campus/College High', + 44: 'Campus/College Low', + 45: 'Hospital/Civic/Other Institutional', + 46: 'Urban Elementary School', + 47: 'Non-Urban Elementary School', + 48: 'Urban Middle School', + 49: 'Non-Urban Middle School', + 50: 'Urban High School', + 51: 'Non-Urban High School', + 52: 'Urban City Hall', + 53: 'Urban Public Library', + 54: 'Urban Courthouse', + 55: 'Urban Convention Center', + 56: 'Town Civic Complex', + 57: 'Suburban Civic Complex', + 58: 'Town/Branch Library', + 59: 'Church', + 60: 'Military General Catch-All', + 61: 'Low Density Commercial', + 62: 'Airport', + 63: 'Park/Recreation', + 64: 'Connected Tourism', + 65: 'Open Space', + 66: 'Health Office', + 67: 'Transit/Rail Station', + 68: 'Public Infrastructure' + } + } \ No newline at end of file diff --git a/footprint/main/models/keys/db_entity_keys.py b/footprint/main/models/keys/db_entity_keys.py new file mode 100644 index 000000000..ce121ee79 --- /dev/null +++ b/footprint/main/models/keys/db_entity_keys.py @@ -0,0 +1,62 @@ +__author__ = 'calthorpe_associates' + + +class DbEntityKeys(object): + + #default urbanfootprint tables + DB_ABSTRACT_BASE_FEATURE = 'base_feature' + DB_ABSTRACT_BASE_PARCEL_FEATURE = 'base_parcel_feature' + DB_ABSTRACT_BASE_DEMOGRAPHIC_FEATURE = 'base_demographic_feature' + DB_ABSTRACT_DEVELOPABLE = 'developable' + DB_ABSTRACT_INCREMENT_FEATURE = 'increments' + DB_ABSTRACT_END_STATE_FEATURE = 'end_state' + DB_ABSTRACT_END_STATE_DEMOGRAPHIC_FEATURE = 'end_state_demographic_feature' + DB_ABSTRACT_FUTURE_SCENARIO_FEATURE = 'future_scenario_feature' + DB_ABSTRACT_CPAD_HOLDINGS_FEATURE = 'cpad_holdings' + DB_ABSTRACT_CENSUS_BLOCK = 'census_block' + DB_ABSTRACT_CENSUS_BLOCKGROUP = 'census_blockgroup' + DB_ABSTRACT_CENSUS_TRACT = 'census_tract' + DB_ABSTRACT_FISCAL_FEATURE = 'fiscal' + + #VMT tables include both base and future versions + DB_ABSTRACT_VMT_FEATURE = 'vmt_feature' + DB_ABSTRACT_VMT_QUARTER_MILE_BUFFER_FEATURE = 'vmt_quarter_mile_buffer_feature' + DB_ABSTRACT_VMT_ONE_MILE_BUFFER_FEATURE = 'vmt_one_mile_buffer_feature' + DB_ABSTRACT_VMT_VARIABLE_BUFFER_FEATURE = 'vmt_variable_buffer_feature' + + DB_ABSTRACT_VMT_FUTURE_TRIP_LENGTHS_FEATURE = 'vmt_future_trip_lengths_feature' + DB_ABSTRACT_VMT_BASE_TRIP_LENGTHS_FEATURE = 'vmt_base_trip_lengths_feature' + + #scag specific tables + DB_ABSTRACT_SCAG_EXISTING_LAND_USE_PARCEL_SOURCE = 'scag_existing_land_use_parcels' + DB_ABSTRACT_GENERAL_PLAN_FEATURE = 'general_plan_parcels' + DB_ABSTRACT_PRIMARY_SPZ_SOURCE = 'primary_spz' + DB_ABSTRACT_JURISDICTION_BOUNDARY = 'jurisdiction_boundary' + DB_ABSTRACT_SPHERE_OF_INFLUENCE = 'sphere_of_influence' + DB_ABSTRACT_FLOODPLAIN = 'floodplain' + DB_ABSTRACT_TIER1_TAZ = 'tier1_taz' + DB_ABSTRACT_TIER2_TAZ = 'tier2_taz' + DB_ABSTRACT_TRANSIT_AREAS = 'transit_areas' + DB_ABSTRACT_PARKS_OPEN_SPACE = 'parks_open_space' + + #sacog specfic tables + DB_ABSTRACT_SACOG_EXISTING_LAND_USE_PARCEL_SOURCE = 'sacog_existing_land_use_parcels' + DB_ABSTRACT_STREAM_FEATURE = 'streams' + DB_ABSTRACT_VERNAL_POOL_FEATURE = 'vernal_pools' + DB_ABSTRACT_WETLAND_FEATURE = 'wetlands' + DB_ABSTRACT_HARDWOOD_FEATURE = 'hardwoods' + DB_ABSTRACT_LIGHT_RAIL_FEATURE = 'light_rail' + DB_ABSTRACT_LIGHT_RAIL_STOPS_FEATURE = 'light_rail_stops' + DB_ABSTRACT_LIGHT_RAIL_STOPS_ONE_MILE_FEATURE = 'light_rail_stops_one_mile' + DB_ABSTRACT_LIGHT_RAIL_STOPS_HALF_MILE_FEATURE = 'light_rail_stops_half_mile' + DB_ABSTRACT_LIGHT_RAIL_STOPS_QUARTER_MILE_FEATURE = 'light_rail_stops_quarter_mile' + + #sandag specific tables + DB_ABSTRACT_SANDAG_SCENARIO_B_BOUNDARY = 'scenario_b_boundary' + DB_ABSTRACT_SANDAG_SCENARIO_C_BOUNDARY = 'scenario_c_boundary' + DB_ABSTRACT_SANDAG_2050_RTP_TRANSIT_NETWORK = 'rtp_2050_transit_network' + DB_ABSTRACT_SANDAG_2050_RTP_TRANSIT_STOPS = 'rtp_2050_transit_stops' + + DB_PLACETYPES = 'placetypes' + + diff --git a/footprint/main/models/keys/keys.py b/footprint/main/models/keys/keys.py new file mode 100644 index 000000000..594a44ac3 --- /dev/null +++ b/footprint/main/models/keys/keys.py @@ -0,0 +1,106 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.keys.built_form_keys import BuiltFormKeys +from footprint.main.models.keys.db_entity_keys import DbEntityKeys +from footprint.main.models.keys.srs_keys import SRSKeys +from footprint.main.models.keys.template_keys import TemplateKeys + +__author__ = 'calthorpe_associates' + + +class Keys(DbEntityKeys, BuiltFormKeys, TemplateKeys, SRSKeys): + """ + Keys representing names by which to identity Policies, DBEntities, DbEntityInterests, Media, and Media content types. + These values could be represented as instantiations of a reference table, but for now this is adequate. Some of these will also be represented as classes, which might cause the key to be embedded in the class definition instead. + """ + + class Fab(object): + """ + This inner class is declared on all Keys subclasses (which inherit from this class.) + It allows constant key definitions in the form FOO = Fab.ricate('foo'), leading to FOO == 'class_prefix__foo' + The subclasses is needed only because a class can't reference itself in its declaration + """ + + @classmethod + def ricate(cls, key): + """ + Creates the key by joining the prefix to the given key. + :param cls: + :param key: + :return: + """ + return "__".join(cls.prefixes() + [key]) + + @classmethod + def prefixes(cls): + prefix = cls.prefix() + return super(Keys.Fab, cls).prefixes() if hasattr(super(Keys.Fab, cls), 'prefixes') else [] + [ + prefix] if prefix else [] + + @classmethod + def prefix(cls): + """ + Prepends a prefix to a key value. This will use super to join parent prefixes with an '__'. If no + prefix is specified the class will not contribute one + :return: + """ + return None + + @classmethod + def prefix(cls): + return cls.Fab.prefix() + + POLICY_TRANSIT = 'transit' + + CONTENT_TYPE_XML = 'xml', + CONTENT_TYPE_SLD = 'sld' + CONTENT_TYPE_PNG = 'png' + CONTENT_TYPE_PYTHON = 'python' + CONTENT_TYPE_CSS = 'css' + CONTENT_TYPE_JSON = 'json' + + INTEREST_OWNER = 'owner' # Ownership of a DBEntity + INTEREST_DEPENDENT = 'dependent' # State of ConfigEntity is invalidated when db_entity changes + INTEREST_FOLLOWER = 'follower' # ConfigEntity listens for update signals, but doesn't invalidate + + # Various SortType keys. + SORT_TYPE_KEY = 'key' + SORT_TYPE_NAME = 'name' + + # These are used to sort PresentationMedia of Library instances + SORT_TYPE_PRESENTATION_MEDIA_DB_ENTITY_KEY = 'presentation_media_db_entity_key' + SORT_TYPE_PRESENTATION_MEDIA_DB_ENTITY_NAME = 'presentation_media_db_entity_name' + SORT_TYPE_PRESENTATION_MEDIA_MEDIUM_KEY = 'presentation_media_medium_key' + SORT_TYPE_PRESENTATION_MEDIA_MEDIUM_NAME = 'presentation_media_medium_name' + + GLOBAL_CONFIG_KEY = 'global' + GLOBAL_CONFIG_NAME = 'Global Config' + + # Geoserver presentations that associate a ConfigEntity with xml, sld, etc media + PRESENTATION_GEOSERVER = 'presentation_geoserver' + # The default layer library for the map presentation + LAYER_LIBRARY_DEFAULT = 'library_default' + + # Represents a global medium that can be used as a default association in PresentationMedium instances until they + # are associated to an instance that represents an actual medium + MEDIUM_DEFAULT = 'medium_default' + + STYLE_BUILT_FORM = 'built_form_cartoCSS' + + diff --git a/footprint/main/models/keys/srs_keys.py b/footprint/main/models/keys/srs_keys.py new file mode 100644 index 000000000..f5484632a --- /dev/null +++ b/footprint/main/models/keys/srs_keys.py @@ -0,0 +1,8 @@ +__author__ = 'calthorpe_associates' + + +class SRSKeys(object): + + SRS_GOOGLE = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m \ + +nadgrids=@null +wktext +no_defs +over" + SRS_4326 = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs" \ No newline at end of file diff --git a/footprint/main/models/keys/template_keys.py b/footprint/main/models/keys/template_keys.py new file mode 100644 index 000000000..70a826bbc --- /dev/null +++ b/footprint/main/models/keys/template_keys.py @@ -0,0 +1,34 @@ +__author__ = 'calthorpe_associates' + +class TemplateKeys(object): + + TEMPLATE_BUILT_FORMS_TILESTACHE = 'template_built_forms_tilestache' + TEMPLATE_BUILT_FORMS_TILESTACHE_CSS = 'template_built_forms_tilestache_css' + TEMPLATE_BUILT_FORMS_MAPNIK_CSS = 'template_built_forms_mapnik_css' + TEMPLATE_BUILT_FORMS_STANDARD_CSS = 'template_built_forms_polymaps_css' + + # Used for features that render increment attributes (e.g. CoreIncrementFeature) + TEMPLATE_INCREMENTS_TILESTACHE = 'template_increments_tilestache' + TEMPLATE_INCREMENTS_TILESTACHE_CSS = 'template_increments_tilestache_css' + TEMPLATE_INCREMENTS_MAPNIK_CSS = 'template_increments_tilestache_css' + TEMPLATE_INCREMENTS_STANDARD_CSS = 'template_increments_tilestache_css' + + # used for the primary base feature + TEMPLATE_PRIMARY_BASE_LAND_USE_TILESTACHE = 'template_primary_base_land_use_tilestache' + TEMPLATE_PRIMARY_BASE_LAND_USE_TILESTACHE_CSS = 'template_primary_base_land_use_tilestache_css' + TEMPLATE_PRIMARY_BASE_LAND_USE_MAPNIK_CSS = 'template_primary_base_land_use_mapnik_css' + TEMPLATE_PRIMARY_BASE_LAND_USE_STANDARD_CSS = 'template_primary_base_land_use_polymaps_css' + + TEMPLATE_CENSUS_BLOCK_TILESTACHE = 'template_census_block_tilestache' + TEMPLATE_CENSUS_BLOCK_TILESTACHE_CSS = 'template_census_block_tilestache_css' + TEMPLATE_CENSUS_BLOCK_MAPNIK_CSS = 'template_census_block_mapnik_css' + TEMPLATE_CENSUS_BLOCK_STANDARD_CSS = 'template_census_block_polymaps_css' + + TEMPLATE_CENSUS_BLOCKGROUP_TILESTACHE = 'template_census_blockgroup_tilestache' + TEMPLATE_CENSUS_BLOCKGROUP_TILESTACHE_CSS = 'template_census_blockgroup_tilestache_css' + TEMPLATE_CENSUS_BLOCKGROUP_MAPNIK_CSS = 'template_census_blockgroup_mapnik_css' + TEMPLATE_CENSUS_BLOCKGROUP_STANDARD_CSS = 'template_census_blockgroup_polymaps_css' + + TEMPLATE_FEATURE_SELECTION = 'template_feature_selection' + TEMPLATE_SELECTION_LAYER_MAPNIK_CSS = 'template_selection_layer_mapnik_css' + diff --git a/footprint/main/models/presentation/__init__.py b/footprint/main/models/presentation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/presentation/built_form_example.py b/footprint/main/models/presentation/built_form_example.py new file mode 100644 index 000000000..03e776141 --- /dev/null +++ b/footprint/main/models/presentation/built_form_example.py @@ -0,0 +1,42 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models +from picklefield import PickledObjectField +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.key import Key +from footprint.main.mixins.name import Name + +__author__ = 'calthorpe_associates' + + +class BuiltFormExample(Key, Name): + objects = GeoInheritanceManager() + + url_aerial = models.CharField(max_length=200, null=True, blank=True) + url_street = models.CharField(max_length=200, null=True, blank=True) + content = PickledObjectField(null=True) + + def __unicode__(self): + return "{0} {1}, url:{2}, content_type:{3}".format(Key.__unicode__(self), + Name.__unicode__(self), self.url, self.content_type) + + class Meta(object): + app_label = 'main' + verbose_name_plural = 'media' diff --git a/footprint/main/models/presentation/chart.py b/footprint/main/models/presentation/chart.py new file mode 100644 index 000000000..b15eabee2 --- /dev/null +++ b/footprint/main/models/presentation/chart.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.main.models.presentation.result import Result + +__author__ = 'calthorpe_associates' + +class Chart(Result): + """ + Relational data configured for display as a chart + """ + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/presentation/combined_config_entity.py b/footprint/main/models/presentation/combined_config_entity.py new file mode 100644 index 000000000..79b751768 --- /dev/null +++ b/footprint/main/models/presentation/combined_config_entity.py @@ -0,0 +1,84 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.geos import MultiPolygon +from footprint.main.models.config.global_config import global_config_singleton +from footprint.main.lib.functions import flat_map, remove_keys, merge, unique +from footprint.main.models.config.config_entity import ConfigEntity + +__author__ = 'calthorpe_associates' + + +class CombinedConfigEntity(ConfigEntity): + """ + A ConfigEntity subclass that is used to combine several ConfigEntities, such as sibling Scenarios, to use as + the basis of a Presentation + """ + + config_entities = [] + + def __init__(self, *args, **kwargs): + # Remove any kwargs specific to this class before calling super + new_kwargs = merge( + remove_keys( + kwargs, + ['config_entities', 'parent_config_entity']), + {'parent_config_entity': kwargs.get('parent_config_entity', global_config_singleton())}) + super(CombinedConfigEntity, self).__init__(*args, **new_kwargs) + # Set the parent_config_entity, which will function as the parent, if needed. Also reference the config + # entities whose combined data will makeup this instances ConfigEntity properties + if kwargs['parent_config_entity']: + self.config_entities = kwargs['parent_config_entity'].children() + elif 'config_entities' in kwargs: + # Just default to the GlobalConfig singleton. This could be smarter and traverse up all the config_entity + # parents to find the common one. + self.config_entities = kwargs['config_entities'] + + def save(self, force_insert=False, force_update=False, using=None): + """ + Overrides the default save to merge the self.config_entities properties after doing an initial save + :param force_insert: + :param force_update: + :param using: + :return: + """ + + # First save to create a pk + super(CombinedConfigEntity, self).save(force_insert, force_update, using) + # Add unique instances to each collection from the config_entities. References to the parent_config_entity's + # instances will automatically be adopted first. + for method in ConfigEntity.INHERITABLE_COLLECTIONS: + # get the add_method or add_method_through method name + getattr(self, '_add_{0}'.format(method))( + *unique( + flat_map( + lambda config_entity: getattr(config_entity, 'computed_{0}'.format(method))(), + self.config_entities), + lambda instance: instance.pk)) + + # Combine the bounds of the config_entities to make this instance's bounds + self.bounds = MultiPolygon(map(lambda config_entity: config_entity.bounds.cascaded_union, self.config_entities)) + + @classmethod + def lineage(cls): + """ + A PresentationConfig can have any ConfigEntity for a parent + :param cls: + :return: + """ + return ConfigEntity.__subclasses__() + + class Meta(object): + app_label = 'main' diff --git a/footprint/main/models/presentation/geo_library.py b/footprint/main/models/presentation/geo_library.py new file mode 100644 index 000000000..176b83334 --- /dev/null +++ b/footprint/main/models/presentation/geo_library.py @@ -0,0 +1,32 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.models.geospatial.db_entity import DbEntity + +__author__ = 'calthorpe_associates' + + +class GeoLibrary(models.Model): + """ + A collection of DbEntities + """ + entities = models.ManyToManyField(DbEntity, through='GeoLibraryCatalog') + + class Meta(object): + app_label = 'main' \ No newline at end of file diff --git a/footprint/main/models/presentation/geo_library_catalog.py b/footprint/main/models/presentation/geo_library_catalog.py new file mode 100644 index 000000000..4ce0c99de --- /dev/null +++ b/footprint/main/models/presentation/geo_library_catalog.py @@ -0,0 +1,34 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.models.geospatial.db_entity import DbEntity +from footprint.main.models.presentation.geo_library import GeoLibrary + +__author__ = 'calthorpe_associates' + +class GeoLibraryCatalog(models.Model): + """ + A collection of DbEntities for orderable display + """ + entity = models.ForeignKey(DbEntity) + geo_library = models.ForeignKey(GeoLibrary) + position = models.IntegerField() + + class Meta(object): + app_label = 'main' \ No newline at end of file diff --git a/footprint/main/models/presentation/grid.py b/footprint/main/models/presentation/grid.py new file mode 100644 index 000000000..f3de6345b --- /dev/null +++ b/footprint/main/models/presentation/grid.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.main.models.presentation.result import Result + +__author__ = 'calthorpe_associates' + +class Grid(Result): + """ + Relational data configured for display as a grid (i.e. table) + """ + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/presentation/layer.py b/footprint/main/models/presentation/layer.py new file mode 100644 index 000000000..8ca311081 --- /dev/null +++ b/footprint/main/models/presentation/layer.py @@ -0,0 +1,55 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from django.db.models.fields.related import ForeignKey + +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.name import Name +from footprint.main.models.presentation.presentation_medium import PresentationMedium +from footprint.main.models.tag import Tag + +__author__ = 'calthorpe_associates' + +class Layer(PresentationMedium, Name): + + """ + Relational data configured for display as a map layer + """ + objects = GeoInheritanceManager() + + # Reference to the origin of this layer if it was cloned + origin_instance = ForeignKey('Layer', null=True) + # Indicates along with the origin_intance that the Layer is created from the origin_instance's selection + create_from_selection = models.BooleanField(default=False) + + def update_tags(self): + """ + Tag an untagged layer with the default tag, based on its config_entity and db_entity + """ + if not len(self.tags.all()) > 0: + # Tag the layer with defaults + self.tags.add(*[ + Tag.objects.update_or_create( + tag=self.presentation.config_entity.db_entity_owner(self.db_entity_interest.db_entity).key)[0] + ]) + + class Meta(object): + app_label = 'main' + + diff --git a/footprint/main/models/presentation/layer_chart.py b/footprint/main/models/presentation/layer_chart.py new file mode 100644 index 000000000..05b15f1de --- /dev/null +++ b/footprint/main/models/presentation/layer_chart.py @@ -0,0 +1,33 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.models.presentation.layer import Layer +from footprint.main.models.presentation.chart import Chart + +__author__ = 'calthorpe_associates' + +class LayerChart(Chart): + """ + A chart that has a layer configuration to allow subcharts to be displayed geographically + """ + layer = models.ForeignKey(Layer, null=False) + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/presentation/layer_library.py b/footprint/main/models/presentation/layer_library.py new file mode 100644 index 000000000..5cf6f1933 --- /dev/null +++ b/footprint/main/models/presentation/layer_library.py @@ -0,0 +1,43 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.presentation.layer import Layer +from footprint.main.models.presentation.presentation import Presentation + +__author__ = 'calthorpe_associates' + + +class LayerLibrary(Presentation): + """ + A library that organizes the db_entities of its inherited Presentation that are associated via presentation's media. A Library supports sorting, filtering, hiding of the underlying data. + """ + objects = GeoInheritanceManager() + + @property + def layers(self): + return self.presentationmedium_set.filter(deleted=False).select_subclasses() + + @property + def presentation_media(self): + return self.layers + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/presentation/layer_selection.py b/footprint/main/models/presentation/layer_selection.py new file mode 100644 index 000000000..91fbefe8a --- /dev/null +++ b/footprint/main/models/presentation/layer_selection.py @@ -0,0 +1,341 @@ +from decimal import Decimal +from django.contrib.auth.models import User +from django.contrib.gis.db import models +from django.contrib.gis.geos import GEOSGeometry +from django.db.models.query import QuerySet +from geojson import loads +from jsonify.templatetags.jsonify import jsonify +from picklefield import PickledObjectField +from footprint.main.lib.functions import flat_map, any_true, map_to_dict, dual_map_to_dict, map_dict, map_dict_value +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.presentation.layer import Layer +from footprint.main.models.presentation.medium import Medium +from footprint.main.utils.query_parsing import parse_query +from footprint.main.models.database.information_schema import InformationSchema +from footprint.main.utils.dynamic_subclassing import dynamic_model_class, create_tables_for_dynamic_classes, drop_tables_for_dynamic_classes +from footprint.main.utils.utils import parse_schema_and_table, clear_many_cache_on_instance_field, normalize_null, get_property_path + +__author__ = 'calthorpe_associates' + +DEFAULT_GEOMETRY = GEOSGeometry('MULTIPOLYGON EMPTY') + +class LayerSelection(models.Model): + """ + An abstract feature class that represents selection layers corresponding to a ConfigEntity and user in the system. + Each instance has a Layer reference which references FeatureClass via its db_entity_key, + and this FeatureClass's instances are computed and stored in selected_features based on the value of geometry. + The selected_feature property returns a query of the FeatureClass. No Feature subclass specific field exists + in this model instead features are simply cached in a pickedobjectfield. + """ + objects = GeoInheritanceManager() + + # The user to whom the selection belongs + user = models.ForeignKey(User) + + # The layer which this LayerSelection represents + #layer = models.ForeignKey(Layer) + + # The geometry of the selection. If accumulating selections we can just add polygons together. + geometry = models.GeometryField(null=True, default=DEFAULT_GEOMETRY) + + aggregates = PickledObjectField(null=True) + filter = PickledObjectField(null=True) + joins = PickledObjectField(null=True) + group_bys = PickledObjectField(null=True) + + # This dictionary of the raw query strings, stored so the UI can show them again + query_strings = PickledObjectField(null=True, default=lambda: dict(aggregates_string=None, filter_string=None, group_by_string=None)) + + # The selected Feature instances of the feature class/table that the layer represents. + # This field is always written to when the bounds property is set. + # We model this as a blob because it never needs to be queried in part. Also, it's difficult to get Django + # to correctly create a ManyToMany dynamic field of a dynamic Feature subclass. The declaration works fine, + # but resolving field names tends to run into problems with the name_map cache that Django uses to track field + # relationships. + # This class is created dynamically in the dynamic class creation below + #features=models.ManyToManyField(feature_class, through=dynamic_through_class, related_name=table_name) + + # The ordered list of field names matching the results + result_fields = PickledObjectField(null=True) + # A lookup from the field name to a human readable title + result_field_title_lookup = PickledObjectField(null=True, default=lambda: {}) + + # Stores summary results, i.e. results produced by query aggregation. These are always a list of dicts + summary_results = PickledObjectField(null=True) + # The ordered list of field names matching the summary results + summary_fields = PickledObjectField(null=True) + # A lookup from the field name to a human readable title + summary_field_title_lookup = PickledObjectField(null=True, default=lambda: {}) + + # The sql used to create the results, for debugging + query_sql = None + # The sql used to create the summary results, for debugging + summary_query_sql = None + + @staticmethod + def cleanup_title(key): + parts = key.split('__') + if len(parts) > 2: + return '_'.join(key.split('__')[2:]) + else: + return '_'.join(parts) + + def update_features(self, query_result): + """ + Updates the features property ManyToMany with the features of the given QuerySet or results list + :param query_result: + :return: + """ + self.clear_features() + for layer_selection_feature in map( + # Create the through class instance. In the future this will hold other info about the selection, + # Such as the current state of variou attributes to allow undo/redo, etc + lambda feature: self.features.through( + feature=feature, + layer_selection=self + ), query_result.all()): + layer_selection_feature.save() + + + def clear_features(self): + self.features.through.objects.all().delete() + + def update_summary_results(self, query_result): + """ + Updates the summary results with the given QuerySet or results list + :param self: + :param query_result: + :return: + """ + + if isinstance(query_result, QuerySet): + # Find aggregate and normal field names + aggregate_names = query_result.aggregate_names if hasattr(query_result, 'aggregate_names') else [] + self.summary_fields = (query_result.field_names if hasattr(query_result, 'field_names') else []) + aggregate_names + # Find aggregate and normal field titles + aggregate_titles = map_dict(lambda key, value: self.cleanup_title(key), query_result.query.aggregates) if hasattr(query_result.query, 'aggregates') else [] + titles = map(lambda tup: self.cleanup_title(tup[1]), query_result.query.select) + aggregate_titles + # Create a lookup from field name to title + self.summary_field_title_lookup = dual_map_to_dict(lambda key, value: [key, value], self.summary_fields, titles) + elif len(query_result) > 0: + # For single row aggregates. TODO figure out who to extract the names from the query + self.summary_fields = query_result[0].keys() + self.summary_field_title_lookup = map_to_dict(lambda key: [key, key], self.summary_fields) + + self.summary_results = map(lambda result: self.process_summary_result(result), query_result) + self.save() + + def clear_summary_results(self): + self.summary_results = None + + @property + def unique_id(self): + return '%s_%s' % (self.user.id, self.layer.id) + + @staticmethod + def process_summary_result(result): + return map_dict_value( + lambda value: round(value, 2) if value and isinstance(value, (float, Decimal)) else value, + result) + + @property + def bounds(self): + """ + Always the same as the geometry field for read-access + :return: + """ + return loads(self.geometry.json) + + @bounds.setter + def bounds(self, value): + """ + Convert the bounds from JSON to the GEOSGeometry format + bounds is a python getter/setter that sets the geometry field + :param value: geojson python object + :return: + """ + # TODO need for geometry + if value and len(value.keys()) > 0: + try: + self.geometry = GEOSGeometry(jsonify(value)) + except: + # TODO log warning + self.geometry = GEOSGeometry('MULTIPOLYGON EMPTY') + else: + self.geometry = GEOSGeometry('MULTIPOLYGON EMPTY') + + @property + def selected_features(self): + """ + A parsed query from a tokenized python dict. + :return: + """ + + try: + return self.features.all() + except: + # Fix a terrible Django manyToMany cache initialization bug by clearing the model caches + clear_many_cache_on_instance_field(self.features) + return self.features.all() + + @property + def selection_extent(self): + """ + The bounds of the current selection or the whole thing if no selection + """ + return self.selected_features.extent_polygon() if len(self.selected_features) > 0 else self.feature_class().objects.extent_polygon() + + def clear(self): + """ + Clears the geometry and the selected features + :return: + """ + self.geometry = None + self._clear_features() + + def _clear_features(self): + """ + Clears the features instances + :greturn: + """ + + self.features.delete() + + def feature_class(self): + """ + The feature class corresponding to the instance's layer + :return: + """ + config_entity = self.__class__.config_entity + return config_entity.feature_class_of_db_entity_key(self.layer.db_entity_key) + + def create_query_set(self, query_set, previous_attributes={}): + # Filter by bounds if present + bounded_query_set = self.feature_class().objects.filter(wkb_geometry__intersects=self.geometry) \ + if self.geometry != DEFAULT_GEOMETRY else query_set + # Filter by filter if present + if self.filter: + # If filters have been specified then perform the query. + return parse_query(self.config_entity, bounded_query_set, filters=self.filter, joins=self.joins) + elif self.geometry != DEFAULT_GEOMETRY: + # Return the result unless neither bounded or filtered. + # Specifying nothing results in an empty query set (i.e. there is no select all) + return parse_query(self.config_entity, bounded_query_set, joins=self.joins) + elif self.joins: + return parse_query(self.config_entity, bounded_query_set, joins=self.joins) + else: + query_set.none() + + def create_summary_query_set(self, query_set, previous_attributes={}): + if not self.aggregates and not self.group_bys: + return None + + # Filter by bounds if present + bounded_query_set = self.feature_class().objects.filter(wkb_geometry__intersects=self.geometry) \ + if self.geometry != DEFAULT_GEOMETRY else query_set + + # If filters have been specified then perform the query. + return parse_query(self.config_entity, bounded_query_set, filters=self.filter, joins=self.joins, + group_bys=self.group_bys, aggregates=self.aggregates) + + def properties_have_changed(self, property_dict, *properties): + return any_true(lambda property: + normalize_null(get_property_path(self, property)) != + normalize_null(get_property_path(property_dict, property)), + properties) + + class Meta(object): + app_label = 'main' + abstract = True + +class LayerSelectionFeature(models.Model): + objects = GeoInheritanceManager() + + # A class name is used to avoid circular dependency + #layer_selection = models.ForeignKey(LayerSelection, null=False) + #feature = models.ForeignKey(Feature, null=False) + #layer_selection = None + #feature = None + medium = models.ForeignKey(Medium, null=True, default=None) + + def __unicode__(self): + return "LayerSelection:{0}, Feature:{1}, Medium:{2}".format(self.layer_selection, self.feature, self.medium) + class Meta(object): + app_label = 'main' + abstract = True + +def get_or_create_dynamic_layer_selection_class_and_table(layer, no_table_creation=False): + """ + Generate a subclass of LayerSelection specific to the layer and use it to create a table + :param layer + :param no_table_creation for debugging, don't create the underlying table + :return: + """ + + config_entity = layer.presentation.subclassed_config_entity + dynamic_class_name = 'LayerSelection{0}'.format(layer.id) + try: + feature_class = config_entity.feature_class_of_db_entity_key(layer.db_entity_key) + except Exception: + # For non feature db_entities, like google maps + return None + + dynamic_through_class = dynamic_model_class( + LayerSelectionFeature, + layer.presentation.subclassed_config_entity.schema(), + 'lsf%s' % layer.id, + class_name='{0}{1}'.format(dynamic_class_name, 'Feature'), + fields=dict( + layer_selection=models.ForeignKey(dynamic_class_name, null=False), + feature=models.ForeignKey(feature_class, null=False), + ) + ) + + # Table is layer specific. Use ls instead of layerselection to avoid growing the schema.table over 64 characters, sigh + table_name = 'ls%s' % layer.id + dynamic_class = dynamic_model_class( + LayerSelection, + # Schema is that of the config_entity + layer.presentation.subclassed_config_entity.schema(), + table_name, + class_name=dynamic_class_name, + # The config_entity instance is a class attribute + class_attrs=dict( + config_entity__id=config_entity.id, + layer__id=layer.id, + override_db=config_entity.db + ), + related_class_lookup=dict( + config_entity='footprint.main.models.config.config_entity.ConfigEntity', + layer='footprint.main.models.presentation.layer.Layer'), + fields=dict( + features=models.ManyToManyField(feature_class, through=dynamic_through_class, related_name=table_name) + ) + ) + + # Make sure the tables exist + if not no_table_creation: + create_tables_for_dynamic_classes(dynamic_class, dynamic_through_class) + + return dynamic_class + +def drop_layer_selection_table(layer_selection_class): + """ + Drop the dynamic LayerSelection class table. This should be called whenever the owning layer is deleted + :param layer_selection_class: + :return: + """ + if InformationSchema.objects.table_exists(*parse_schema_and_table(layer_selection_class._meta.db_table)): + drop_tables_for_dynamic_classes(layer_selection_class) + +def layer_selections_of_config_entity(config_entity): + """ + Returns all LayerSelection instances of the ConfigEntity + :param config_entity: + :return: + """ + return flat_map( + lambda layer: list(get_or_create_dynamic_layer_selection_class_and_table(layer, no_table_creation=True)), + Layer.objects.filters(config_entity=config_entity)) + + diff --git a/footprint/main/models/presentation/map.py b/footprint/main/models/presentation/map.py new file mode 100644 index 000000000..23ac547b1 --- /dev/null +++ b/footprint/main/models/presentation/map.py @@ -0,0 +1,30 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.presentation.presentation import Presentation + +__author__ = 'calthorpe_associates' + +class Map(Presentation): + """ + Presents a map configured based on an EntityConfig + """ + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/presentation/medium.py b/footprint/main/models/presentation/medium.py new file mode 100644 index 000000000..c4b74b370 --- /dev/null +++ b/footprint/main/models/presentation/medium.py @@ -0,0 +1,49 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models +from picklefield import PickledObjectField +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.deletable import Deletable +from footprint.main.mixins.key import Key +from footprint.main.mixins.name import Name + +__author__ = 'calthorpe_associates' + + +class Medium(Key, Name, Deletable): + objects = GeoInheritanceManager() + + url = models.CharField(max_length=200, null=True, blank=True) + content_type = models.CharField(max_length=20, null=True, blank=True) + content = PickledObjectField(null=True) + + def __unicode__(self): + return "{0} {1}, url:{2}, content_type:{3}".format(Key.__unicode__(self), + Name.__unicode__(self), self.url, self.content_type) + + def render_attributed_content(self, medium_context, content_key=None): + return self.content + + def render_content(self, medium_context, content_key=None): + return self.content + + class Meta(object): + app_label = 'main' + verbose_name_plural = 'media' diff --git a/footprint/main/models/presentation/painting.py b/footprint/main/models/presentation/painting.py new file mode 100644 index 000000000..e85eb1030 --- /dev/null +++ b/footprint/main/models/presentation/painting.py @@ -0,0 +1,30 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.presentation.map import Map + +__author__ = 'calthorpe_associates' + +class Painting(Map): + """ + A map that supports layer editing + """ + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/presentation/presentation.py b/footprint/main/models/presentation/presentation.py new file mode 100644 index 000000000..c859a7dfc --- /dev/null +++ b/footprint/main/models/presentation/presentation.py @@ -0,0 +1,74 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models +from model_utils.managers import InheritanceManager +from picklefield import PickledObjectField +from footprint.main.lib.functions import first +from footprint.main.mixins.deletable import Deletable +from footprint.main.mixins.name import Name +from footprint.main.mixins.scoped_key import ScopedKey +from footprint.main.models.config.config_entity import ConfigEntity + +__author__ = 'calthorpe_associates' + +class Presentation(Name, ScopedKey, Deletable): + """ + Creates a presentation, such as a map, result page, or report specifc to a ConfigEntity. + The presentation is concerned with the visualization of the properties of the ConfigEntity. + """ + + # Presentations have sublasses--enable subclass querying + objects = InheritanceManager() + + # Stores a serialized instance of a Configuration + configuration = PickledObjectField(null=True) + + @property + def presentation_media(self): + raise "Must overload this in subclass" + + config_entity = models.ForeignKey('ConfigEntity', null=False) + @property + def subclassed_config_entity(self): + """ + Resolves the config_entity to its subclass version. This garbage should all be done elegantly by Django, + maybe in the newest version. Otherwise refactor to generalize + :return: + """ + return ConfigEntity._subclassed_config_entity(self.config_entity) + + def db_entities(self): + """ + Returns all DbEntities associated to the presentation via PresentationMedia instance. This will always be + a subset of the config_entity.computed_db_entities(). Since the PresentationMedia's db_entity_key implies + the DbEntity that is selected among two or more of the same key, only one DbEntity per key is returned, + the selected or only one + :return: + """ + return self.config_entity().computed_db_entities().filter(key__in= + map(lambda presentation_media: presentation_media.db_entity_key, + self.presentationmedium_set.exclude(db_entity_key__isnull=True))) + + def __unicode__(self): + return "{0}, {1}".format(Name.__unicode__(self), ScopedKey.__unicode__(self)) + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/presentation/presentation_configuration.py b/footprint/main/models/presentation/presentation_configuration.py new file mode 100644 index 000000000..bcf992175 --- /dev/null +++ b/footprint/main/models/presentation/presentation_configuration.py @@ -0,0 +1,102 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from picklefield import PickledObjectField +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.name import Name +from footprint.main.mixins.scoped_key import ScopedKey +from footprint.main.utils.utils import resolve_model + + +class PresentationConfiguration(ScopedKey, Name): + """ + Configures what db_entities are representation as PresentationMedia in a presentation, and which of those are + initially visible in the presentation. This class will likely add all kinds of other configuration options. + Everything is stored in a PickledObjectField for flexibility + """ + objects = GeoInheritanceManager() + + data = PickledObjectField() + + class Meta(object): + app_label = 'main' + + +class ConfigurationData(object): + def __init__(self, **kwargs): + for (k, v) in kwargs.items(): + setattr(self, k, v) + + presentation_media_configurations = [] + + +class PresentationMediumConfiguration(object): + def __init__(self, **kwargs): + for (k, v) in kwargs.items(): + setattr(self, k, v) + db_entity_key = None + built_form_set_key = None + # Optional sorting priority from 1 to 100. 1 is highest priority. 0 is equivalent to no priority + sort_priority = 0 + # Optional list of attributes of the instance. Listed from highest to lowest priority. Unlisted attributes will + # be lower priority and sorted alphabetically + attribute_sort_priority = [] + # Optionally store a class name here to scope the instance to limit this configuration to the class/subclasses of the + # given config entity + # This needs to be a string since it will be persisted + scope = None + # A list of Tag objects that categorize the instance + tags = [] + + @property + def class_scope(self): + """ + Resolve the actual model class, since it's non-trivial to store in the database + :return: + """ + model = resolve_model('main.{0}'.format(self.scope)) + if not model: + raise Exception("Could not resolve model: " + 'main.{0}'.format(self.scope)) + return model + + +class LayerConfiguration(PresentationMediumConfiguration): + """ + The data configuration for the Layer instances + """ + + # If True, make the layer initially visible in the UI. Default False + visible = False + # A list of class/table attributes that should be stylable by the user + # The name used should match the attribute name on the model, whether on the main model or a related model + visible_attributes = [] + # A dict that maps visible attributes to the column name produced by the raw sql query + # This allows us to add an alias to the column to match the attribute name that we expect + column_alias_lookup = {} + # One of the visible_attributes to style the first time the layer is shown + # If this isn't specifed then the first listed visible_attribute is used + default_attribute = None + # A dictionary that indicates what keys are needed to fill out the Layer's css template. These keys should + # have default values and may be single values, lists, ranges, or dict themselves. The structure is set as + # the context of the Layer's Template's TemplateContext. This dict is then copied to the Layer's context property + # where its values can be modified by a user to customize the layer styling + template_context_dict = dict() + # The class upon which to create the template for the layer + # This is only used by import layer_configurations since layer configurations derive + # their style class from the Feature class that they represent + # import layer_configurations will generally just set this to Feature so to match + # the generic Feature style files + style_class = None + + + + + diff --git a/footprint/main/models/presentation/presentation_medium.py b/footprint/main/models/presentation/presentation_medium.py new file mode 100644 index 000000000..e77f9dc7a --- /dev/null +++ b/footprint/main/models/presentation/presentation_medium.py @@ -0,0 +1,125 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from model_utils.managers import InheritanceManager +from picklefield import PickledObjectField +from footprint.main.mixins.deletable import Deletable +from medium import Medium +from footprint.main.mixins.tags import Tags + +__author__ = 'calthorpe_associates' + +class PresentationMedium(Tags, Deletable): + """ + Links media to a PresentationConfig and also links the medium to a db_entity of the presentation_config via + StyleConfig instances + """ + objects = InheritanceManager() + #objects = CustomQuerySet.as_manager() + + presentation = models.ForeignKey('Presentation') + medium = models.ForeignKey(Medium) + + # Used to indicate whether the instance is currently visible in the presentation. This is useful for a layers + # on a map, charts, etc., when some need to be hidden + visible = models.BooleanField(default=True) + visible_attributes = PickledObjectField(null=True) + + # An optional key reference to a DbEntry known by the presentation's config_entity via a DbEntityInterest. + # The referenced DbEntity indicates the this instance is associated with DbEntity. For example, if the + # DbEntity is a geographic table, the medium might be the Style medium that decorates that table as a visible Layer. + # Multiple media and thus PresentationMedia instance may have the same db_entity_key. + # The reason this is used instead of the db_entity ForeignKey so this instance doesn't have to keep up with the + # currently selected DbEntity. Use db_entity() to resolve the selected DbEntity + db_entity_key = models.CharField(max_length=50, null=False, blank=False) + + # When them medium is a Template that requires context dict of wildcard values to produce a complete medium, + # the currently context dict is stored here. It's initial value should be set to medium.template_context.context, + # which is the default context. + # Example: medium = Template(template_context=TemplateContext(context=dict(foo='red', bar='blue')) then + # medium.medium_context would initially equal dict(foo='red', bar='blue') but could subsequently be updated by + # a user to be dict(foo='purple', bar='aqua') + # The dict might be more complex and be keyed by db_entity table attributes in order to style individual attributes + medium_context = PickledObjectField(null=True) + + # Optional configuration meant for non-stylistic or medium related value. For instance, a result graph might store + # it's axis labels and axis increments here + configuration = PickledObjectField(null=True) + + # Optional. When medium is a Template this combines renders the medium.content as a template with medium_context as + # its context. The rendered_medium can take any form. It might be a dict keyed by DbEntity column names and valued + # by CSS, for instance + rendered_medium = PickledObjectField(null=True) + + @property + def db_entity_interest(self): + """ + Returns the ConfigEntity's selected DbEntityInterest of the key self.db_entity_key. + This will be None when a new layer is created that doesn't have an associated + DbEntity instance yet. This case will disappear as soon as we start saving + the new DbEntity before the new Layer. + :return: + """ + return self._db_entity_interest or \ + self.presentation.subclassed_config_entity.computed_db_entity_interests(db_entity__key=self.db_entity_key)[0] + + # We accept a db_entity_interest to allow the PresentationMediumResource to save new + # DbEntityInterests. After saving this field is set back to None + _db_entity_interest = None + @db_entity_interest.setter + def db_entity_interest(self, value): + self._db_entity_interest = value + def save(self, force_insert=False, force_update=False, using=None): + # Clear this. We'll access it from the config_entity after initial save + self._db_entity_interest = None + super(PresentationMedium, self).save(force_insert, force_update, using) + + def __unicode__(self): + return "presentation: {0}, medium: {1}".format(unicode(self.presentation), unicode(self.medium)) + + def query(self): + return self.get_data() + + def get_data(self, **kwargs): + """ + Return the DbEntity data of the PresentationMedium in cases where DbEntities are modeled by a feature class. + The feature class of the config_entity, db_entity combination will either return all results or return + the query defined on the db_entity, if there is one. Optionally provide a group_by clause (see DbEntity for + syntax) + :param kwargs['group_by'] overrides or provides the DbEntity query with a group by--aggregating values and determining + what fields are returned. This will make the return data always a list of dicts. + :param kwargs['values'] overrides or provides the DbEntity query with + :return: + """ + return self.db_entity_interest.db_entity.run_query(self.presentation.subclassed_config_entity, **kwargs) + + def save(self, force_insert=False, force_update=False, using=None): + super(PresentationMedium, self).save(force_insert, force_update, using) + self.update_tags() + + def update_tags(self): + """ + Optionally updates an instance's tags. Overridden by the subclass + """ + pass + + class Meta(object): + app_label = 'main' + verbose_name_plural = 'presentation_media' + diff --git a/footprint/main/models/presentation/report.py b/footprint/main/models/presentation/report.py new file mode 100644 index 000000000..aac2da0f2 --- /dev/null +++ b/footprint/main/models/presentation/report.py @@ -0,0 +1,29 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.presentation.presentation import Presentation + +__author__ = 'calthorpe_associates' + +class Report(Presentation): + """ + A report configuration + """ + + class Meta(object): + app_label = 'main' diff --git a/footprint/main/models/presentation/result.py b/footprint/main/models/presentation/result.py new file mode 100644 index 000000000..8eb5e28d6 --- /dev/null +++ b/footprint/main/models/presentation/result.py @@ -0,0 +1,31 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.presentation.presentation_medium import PresentationMedium + +__author__ = 'calthorpe_associates' + +class Result(PresentationMedium): + """ + Relational data displayed as a result for reporting + """ + objects = GeoInheritanceManager() + + class Meta(object): + app_label = 'main' diff --git a/footprint/main/models/presentation/result_library.py b/footprint/main/models/presentation/result_library.py new file mode 100644 index 000000000..901aa2fe3 --- /dev/null +++ b/footprint/main/models/presentation/result_library.py @@ -0,0 +1,41 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.presentation.result import Result +from footprint.main.models.presentation.presentation import Presentation + +__author__ = 'calthorpe_associates' + +class ResultLibrary(Presentation): + """ + A page configured to show charts, grids, maps, etc + """ + objects = GeoInheritanceManager() + + @property + def results(self): + return self.presentationmedium_set.filter(deleted=False).select_subclasses() + + def presentation_media(self): + return self.results + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/presentation/style.py b/footprint/main/models/presentation/style.py new file mode 100644 index 000000000..f915a55de --- /dev/null +++ b/footprint/main/models/presentation/style.py @@ -0,0 +1,39 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.mixins.key import Key +from footprint.main.mixins.name import Name +from model_utils.managers import InheritanceManager +from django.db import models + +__author__ = 'calthorpe_associates' + + +class Style(Key, Name): + """ + Represents a style class that is applied a geographic table column (currently via PresentationMedium). + Style inherits the properties of Medium. It might be better at some point to make Style a sibling of Medium + instead. + """ + + objects = InheritanceManager() + + identifier = models.TextField() + target = models.TextField() + style_property = models.TextField() + + class Meta(object): + app_label = 'main' \ No newline at end of file diff --git a/footprint/main/models/presentation/template.py b/footprint/main/models/presentation/template.py new file mode 100644 index 000000000..f5f953cdf --- /dev/null +++ b/footprint/main/models/presentation/template.py @@ -0,0 +1,73 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.template import Context +from django.template.loader import get_template_from_string +from picklefield.fields import PickledObjectField +from footprint.main.lib.functions import map_dict_to_dict, merge +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.models.presentation.medium import Medium + +__author__ = 'calthorpe_associates' + + +class Template(Medium): + """ + Represents a Django-style template that needs additional data to be a usable Medium. It thus references a + TemplateContext which might be XML, JSON, etc that has been parsed to python and can be used to populate the + template. The Django style template string with wildcards (handlebars) must be stored in the content property. + The template.template_context contains the default python dictionary to use to fill in the handlebars ({{ }}) + in the template. + """ + objects = GeoInheritanceManager() + + template_context = PickledObjectField() + + def render_attributed_content(self, medium_context, content_key=None): + """ + Given the medium_context for a PresentationMedium, render the default template with the medium_context values + :param medium_context: + :param content_key: + :return: + """ + + rendered_attribute_content = map_dict_to_dict( + # For each attribute key get the template dict if one exists and resolve the dict value for the given content_key + # If no content_key exists just take the entire dict + lambda key, value: + [key, + get_template_from_string(value.get(content_key, value).replace('\n', '').replace(' ', '')). + render(Context(medium_context['attributes'])) + ], + self.content['attributes']) if 'attributes' in medium_context else None + # Return a new dict based on the original medium_context with the attributes key/value overridden + return merge(medium_context, dict(attributes=rendered_attribute_content)) + + def render_content(self, medium_context, content_key=None): + """ + Given the medium_context for a PresentationMedium, render the default template with the medium_context values + :param medium_context: + :param content_key: + :return: + """ + return get_template_from_string( + self.content.get(content_key, self.content).replace('\n', '').replace(' ', '').render(Context(medium_context))) if medium_context else None + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/presentation/tilestache_config.py b/footprint/main/models/presentation/tilestache_config.py new file mode 100644 index 000000000..a97403c7f --- /dev/null +++ b/footprint/main/models/presentation/tilestache_config.py @@ -0,0 +1,36 @@ +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager + +__author__ = 'calthorpe_associates' +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models +from picklefield import PickledObjectField + +class TileStacheConfig(models.Model): + """ + Represents the TileStache config dictionary, stored in the config field + """ + objects = GeoInheritanceManager() + name = models.CharField(max_length=50, default='default') + config = PickledObjectField() + enable_caching = models.NullBooleanField(default=True) + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/signals.py b/footprint/main/models/signals.py new file mode 100644 index 000000000..eca1f4273 --- /dev/null +++ b/footprint/main/models/signals.py @@ -0,0 +1,108 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.dispatch import Signal + + +__author__ = 'calthorpe_associates' + +# Defines custom Django Signals that are used by UrbanFootprint models instances to communicate +initialize_media = Signal(providing_args=[]) +initialize_presentations = Signal(providing_args=[]) + +def items_changed(attribute): + def _items_changed(sender, **kwargs): + """ + Listens for m2m signals on ConfigEntity instances. The instance alerts its children of the change so that + can update their adopted collections as needed + :param sender: + :param kwargs: + :return: + """ + donor = kwargs['instance'] + donees = donor.children() + action = kwargs['action'] + if action=='post_add': + # If the donee instance's related list is nonempty, add any that the donor added (empty ones will get the + # change by deferring to the donor's list) + for donee in donees: + manager = getattr(donee, attribute) + if len(manager.all()) > 0: + added = getattr(donor, attribute).filter(pk__in=kwargs['pk_set']) + donee._add(attribute, *added) + elif action=='pre_remove': + # If the donee instance's related list is nonempty, remove any that the donor removed (empty ones will get + # the change by deferring to the donor's list) + for donee in donees: + manager = getattr(donee, attribute) + if len(manager.all()) > 0: + removed = manager.filter(pk__in=kwargs['pk_set']) + donee._remove(attribute, *removed) + elif action=='pre_clear': + # If the donee instance's related list is nonempty, remove all those of the donor (empty ones will get the + # change by deferring to the donor's list) + for donee in donees: + manager = getattr(donee, attribute) + if len(manager.all()) > 0: + donor_manager = getattr(donor, attribute) + removed = donor_manager.all() + donee._remove(attribute, *removed) + return _items_changed + + +def through_item_added(attribute): + def _through_item_added(sender, **kwargs): + if kwargs['created']: + through_item_changed(sender, attribute, 'add', **kwargs) + return _through_item_added + + +def through_item_deleted(attribute): + def _through_item_deleted(sender, **kwargs): + through_item_changed(sender, attribute, 'deleted', **kwargs) + return _through_item_deleted + + +def through_item_changed(sender, attribute, action, **kwargs): + through_instance = kwargs['instance'] + donor = through_instance.config_entity + donees = donor.children() + for donee in donees: + manager = getattr(donee, attribute) + if len(manager.all()) > 0: + # If the donee instance's related list is nonempty, add the through instance + # The config_entity will be updated to that of the done + if action == 'add': + donee._add(attribute, through_instance) + else: + donee._remove(attribute, through_instance) + +# TODO I'm suspicious of performance issues. So leaving these out until really needed. +# They will be needed as soon as we create new DbEntities at the project level, because +# the scenarios need to adopt them +# for attribute in ConfigEntity.INHERITABLE_COLLECTIONS: +# through_class = getattr(ConfigEntity, attribute).through +# # Listen to each through class for changes +# if has_explicit_through_class(ConfigEntity, attribute): +# # through_item_added won't actually do anything unless the item is new kwargs['created']==True +# post_save.connect(through_item_added(attribute), sender=through_class, weak=False) +# pre_delete.connect(through_item_deleted(attribute), sender=through_class, weak=False) +# else: +# m2m_changed.connect(items_changed(attribute), sender=through_class, weak=False) + diff --git a/footprint/main/models/sort_type.py b/footprint/main/models/sort_type.py new file mode 100644 index 000000000..4cf54c630 --- /dev/null +++ b/footprint/main/models/sort_type.py @@ -0,0 +1,38 @@ +# coding=utf-8 +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +from footprint.main.mixins.key import Key +from footprint.main.mixins.name import Name + +__author__ = 'calthorpe_associates' + +class SortType(Key, Name): + """ + SortType instances describes a way of sorting the a Django model collection + """ + objects = GeoInheritanceManager() + + # The order_by attribute upon which to base the search of whatever Many class is being sorted + # This will be applied to the QuerySet using order_by (self.order_by) when instance.sorted(**SortType) is called. + order_by = models.CharField(max_length=100, null=True, blank=False, unique=True, default=None) + + class Meta(object): + app_label = 'main' + diff --git a/footprint/main/models/tag.py b/footprint/main/models/tag.py new file mode 100644 index 000000000..c54186471 --- /dev/null +++ b/footprint/main/models/tag.py @@ -0,0 +1,30 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager + +__author__ = 'calthorpe_associates' + +class Tag(models.Model): + objects = GeoInheritanceManager() + tag = models.CharField(max_length=100, null=False) + class Meta: + app_label='main' + + + diff --git a/footprint/main/models/tasks/__init__.py b/footprint/main/models/tasks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/models/tasks/async_job.py b/footprint/main/models/tasks/async_job.py new file mode 100644 index 000000000..a70607fc9 --- /dev/null +++ b/footprint/main/models/tasks/async_job.py @@ -0,0 +1,29 @@ +from django.contrib.auth.models import User + +__author__ = 'calthorpe_associates' + +from django.db import models +import uuid + +class Job(models.Model): + hashid = models.CharField(max_length=36, unique=True) + task_id = models.CharField(max_length=36) + user = models.ForeignKey(User, related_name='jobs') + type = models.CharField(max_length=32) + status = models.TextField(blank=True) # JSON + created_on = models.DateTimeField(auto_now_add=True) + ended_on = models.DateTimeField(null=True) + data = models.TextField(null=True) + + def __unicode__(self): + return u'Job %s' % self.hashid + + def save(self, *args, **kwargs): + if not self.hashid: + self.hashid = uuid.uuid4() + super(Job, self).save(*args, **kwargs) + + class Meta: + ordering = ['-created_on'] + app_label = 'main' + diff --git a/footprint/main/policy/__init__.py b/footprint/main/policy/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/policy/dial_up.py b/footprint/main/policy/dial_up.py new file mode 100644 index 000000000..cf6e97832 --- /dev/null +++ b/footprint/main/policy/dial_up.py @@ -0,0 +1,39 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.db import models +from footprint.main.models.config.scenario import Scenario + +__author__ = 'calthorpe_associates' + +class DialUp(models.Model): + scenario = models.OneToOneField(Scenario, primary_key=True) + + single_family_detached = models.DecimalField(max_digits=3, decimal_places=2, default=0) + single_family_attached = models.DecimalField(max_digits=3, decimal_places=2, default=0) + multi_family = models.DecimalField(max_digits=3, decimal_places=2, default=0) + + office_employment = models.DecimalField(max_digits=3, decimal_places=2, default=0) + retail_employment = models.DecimalField(max_digits=3, decimal_places=2, default=0) + industrial_employment = models.DecimalField(max_digits=3, decimal_places=2, default=0) + def __unicode__(self): + return unicode("Global Dialup config for %s" % self.scenario.name) + + class Meta: + app_label='main' + diff --git a/footprint/main/policy/setup_scenario_policy.py b/footprint/main/policy/setup_scenario_policy.py new file mode 100644 index 000000000..9c32825c7 --- /dev/null +++ b/footprint/main/policy/setup_scenario_policy.py @@ -0,0 +1,86 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db.models.signals import post_syncdb, post_save, pre_delete +from django.dispatch import receiver +from south.signals import post_migrate +import models +from footprint import main +from footprint.main.models import Scenario +from uf_tools import db_table_exists, executeSQL_now + +__author__ = 'calthorpe_associates' + +@receiver(post_syncdb, sender=models) +@receiver(post_migrate, sender=models) +def on_post_syncdb(sender, **kwargs): + pass + +def on_scenario_post_save(sender, **kwargs): + get_energy_water(kwargs['instance']) + get_dev_acres(kwargs['instance']) + setup_transit_policy(kwargs['instance']) + +def on_scenario_pre_delete(sender, **kwargs): + pass + +def get_energy_water(scenario): + from footprint.main.models.energy_water import EnergyWater + try: + e = EnergyWater.objects.get(scenario=scenario) + except: + e = EnergyWater.objects.create(scenario=scenario) + e.save() + return e + +def get_dev_acres(scenario): + from footprint.main.models.dev_acres import DevAcres + try: + d = DevAcres.objects.get(scenario=scenario) + except: + d = DevAcres.objects.create(scenario=scenario) + d.save() + return d + + +def setup_transit_policy(scenario): + if scenario.year <= 2020: + transit_year = 2020 + elif scenario.year <= 2035: + transit_year = 2035 + else: + transit_year = 2050 + transit_policy = {} + transit_policy['transit_flavor'] = scenario.transit_scenario.lower() + "_" + str(transit_year) + transit_policy['transit_areas'] = "transit_{0}_{1}_{2}".format(scenario.study_area.key.lower().replace(' ', ''), + scenario.transit_scenario.lower(), str(transit_year)) + transit_policy['transit_table'] = scenario.working_schema + "." + scenario.transit_areas + if not db_table_exists(scenario.transit_areas): + print "running transit area sql..." + hsr_radius = str(1609.344 * 2) + standard_radius = str(1609.344) + sql = rawSQL.select_and_buffer_study_area_stops.format(scenario.working_schema, scenario.transit_table, + scenario.study_area.base_year_grid, str(scenario.year), scenario.transit_areas, str(hsr_radius), str(standard_radius), + scenario.transit_flavor) + executeSQL_now(scenario.study_area.inputs_outputs_db, [sql]) + return transit_policy + +# Register Django Signals to respond to synd_db and Scenario persistence +post_syncdb.connect(on_post_syncdb, sender=main.models) +post_save.connect(on_scenario_post_save, sender=Scenario) +pre_delete.connect(on_scenario_pre_delete, sender=Scenario) + diff --git a/footprint/main/publishing/__init__.py b/footprint/main/publishing/__init__.py new file mode 100644 index 000000000..89d7e73ac --- /dev/null +++ b/footprint/main/publishing/__init__.py @@ -0,0 +1,20 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +# List the following imports because they have Django signal receivers that need to fire +from footprint.main.publishing import config_entity_publishing, layer_initialization, layer_publishing, analysis_module_publishing, result_initialization, result_publishing, data_import_publishing, built_form_publishing, tilestache_publishing, db_entity_publishing diff --git a/footprint/main/publishing/analysis_module_publishing.py b/footprint/main/publishing/analysis_module_publishing.py new file mode 100644 index 000000000..7794aabdd --- /dev/null +++ b/footprint/main/publishing/analysis_module_publishing.py @@ -0,0 +1,61 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import logging +from footprint.main.models.analysis_module.vmt_module.vmt import Vmt +from footprint.main.models.analysis_module.fiscal_module.fiscal import Fiscal +from footprint.main.models.analysis_module.core_module.core import Core +from footprint.main.models.config.scenario import FutureScenario + +__author__ = 'calthorpe_associates' + +logger = logging.getLogger(__name__) + +def get_or_create_analysis_modules(config_entity): + """ + Creates a results library and Result instances upon saving a config_entity if they do not yet exist. + :param config_entity + :return: + """ + if isinstance(config_entity, FutureScenario): + for analysis_module_class in [Core, Fiscal, Vmt]: + analysis_module_class.objects.update_or_create( + config_entity=config_entity + ) + + +def on_config_entity_post_save_analysis_modules(sender, **kwargs): + """ + Sync a ConfigEntity's ResultPage presentation + """ + config_entity = kwargs['instance'] + logger.debug("\t\tHandler: on_config_entity_post_save_analysis_module. ConfigEntity: %s" % config_entity.name) + get_or_create_analysis_modules(config_entity) + +def on_db_entity_save(): + """ + respond to whenever a db entity is added or updated + :return: + """ + +def on_config_entity_pre_delete_analysis_modules(sender, **kwargs): + """ + Sync geoserver to a ConfigEntity class after the latter is saved + """ + pass + + diff --git a/footprint/main/publishing/built_form_publishing.py b/footprint/main/publishing/built_form_publishing.py new file mode 100644 index 000000000..20b2e6dfb --- /dev/null +++ b/footprint/main/publishing/built_form_publishing.py @@ -0,0 +1,151 @@ + +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +#from memory_profiler import profile +import os +from django.core.management import call_command +from footprint.main.initialization.built_form.built_form_importer import BuiltFormImporter +import logging +from footprint.client.configuration.fixture import BuiltFormFixture, ConfigEntitiesFixture +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.lib.functions import remove_keys, flatten +from footprint.main.models import BuiltForm +from footprint.main.models.config.global_config import GlobalConfig +from footprint.main.models.config.scenario import Scenario +from footprint.main.models.config.region import Region +from footprint.main.models.built_form.built_form_set import BuiltFormSet +from footprint.main.models.built_form.flat_built_forms import refresh_all_flat_built_forms +from footprint import settings + +logger = logging.getLogger(__name__) + + +#@profile +def on_config_entity_post_save_built_form(sender, **kwargs): + """ + Sync a ConfigEntity's BuiltFormSets + """ + config_entity = kwargs['instance'] + logger.debug("\t\tHandler: on_config_entity_post_save_built_form. ConfigEntity: %s" % config_entity.name) + + if isinstance(config_entity, GlobalConfig) or isinstance(config_entity, Region): + # For now only the GlobalConfig and Regions creates the sets + config_entity.add_built_form_sets(*(set(built_form_sets(config_entity)) - set(config_entity.computed_built_form_sets()))) + + elif isinstance(config_entity, Scenario) and kwargs.get('created', None): + # Scenarios set their selected built_form_set + # This is a bit of hack to lookup the default built_form selection + # TODO This needs to be replaced by a property on the config_entity, such as default_selections, copied from the ConfigEntityFixture + # Looking up the fixture doesn't work for cloned ConfigEntities + config_entities_fixture = resolve_fixture("config_entity", "config_entities", ConfigEntitiesFixture, config_entity.schema()) + scenario_fixtures = filter( + lambda scenario_fixture: scenario_fixture['key'] == config_entity.key, + config_entities_fixture.scenarios(config_entity.parent_config_entity)) + built_form_set_selection_key = scenario_fixtures[0].get('selections', {}).get('built_form_sets', None) \ + if len(scenario_fixtures) > 0 else None + try: + config_entity.select_built_form_set( + config_entity.computed_built_form_sets(key=built_form_set_selection_key)[0] if + built_form_set_selection_key else + config_entity.computed_built_form_sets()[0] + ) + config_entity._no_post_save_publishing = True + config_entity.save() + config_entity._no_post_save_publishing = False + + except Exception: + raise Exception( + "Bad built_form_set configuration for config_entity: {0}. Selected BuiltFormSet key: {1}, All BuiltFormSets".format( + config_entity, built_form_set_selection_key, config_entity.computed_built_form_sets() + )) + + +def built_form_sets(config_entity): + """ + Constructs and persists buildings, buildingtypes, and placetypes and their associates and then returns them all + as a persisted BuiltFormSet. One BuiltFormSet is returned in an array + :param test: if test is set to true, a much more limited set of built forms is created + """ + json_fixture = os.path.join(settings.PROJECT_ROOT, 'built_form_fixture.json') + built_form_fixture = resolve_fixture("built_form", "built_form", BuiltFormFixture, settings.CLIENT, config_entity=config_entity) + + if settings.IMPORT_BUILT_FORMS == 'CSV' or (not os.path.exists(json_fixture)): + logger.info('Importing built forms from csv source') + # Get the fixture scoped for the config_entity + # Create any built_form class sets that are configured for the client at the config_entity's class scope + built_forms_dict = built_form_fixture.built_forms() + built_form_fixture.tag_built_forms(built_forms_dict) + built_forms = flatten(built_forms_dict.values()) + if len(built_forms) > 0: + # Create/Refresh all the flat_built_forms in case anything changed + fixture_file = open(json_fixture, 'w') + refresh_all_flat_built_forms() + logger.debug('recreating fixture at ' + json_fixture) + call_command('dumpdata', 'main.PrimaryComponentPercent', 'main.PlacetypeComponentPercent', + 'main.BuildingUsePercent', 'main.BuiltFormSet', 'main.FlatBuiltForm', indent=2, stdout=fixture_file) + else: + logger.debug("skipping fixture / flat built form generation: nothing changed") + return map( + lambda built_form_set_config: update_or_create_built_form_set(built_form_set_config, built_forms), + built_form_fixture.built_form_sets()) + + elif settings.IMPORT_BUILT_FORMS == 'JSON' and not BuiltForm.objects.count(): + logger.info('Importing built forms from json fixture at ' + json_fixture) + call_command('loaddata', json_fixture) + return {} + + +def update_or_create_built_form_set(built_form_set_config, built_forms): + filtered_built_form_set_dict = remove_keys(built_form_set_config, ['clazz', 'keys', 'client', 'scope']) + built_form_set, created, updated = BuiltFormSet.objects.update_or_create( + **dict( + key=built_form_set_config['key'], + defaults=dict( + **filtered_built_form_set_dict + ) + ) + ) + if not created: + for key, value in filtered_built_form_set_dict.items(): + setattr(built_form_set, key, value) + built_form_set.save() + + existing_built_forms = built_form_set.built_forms.all() + + # for the built_form_sets based on + class_filter = lambda built_form: \ + built_form not in existing_built_forms and isinstance(built_form, built_form_set_config['clazz']) + + importer = BuiltFormImporter() + + built_forms_for_set = built_forms + + if built_form_set_config['clazz']: + built_forms_for_set = filter(class_filter, built_forms_for_set) + + if built_form_set_config['client']: + client = built_form_set_config['client'] + client_built_form_names = [bf.name for bf in importer.load_buildings_csv(client)] + \ + [bf.name for bf in importer.load_buildingtype_csv(client)] + \ + [bf.name for bf in importer.load_placetype_csv(client)] + + client_filter = lambda built_form: \ + built_form not in existing_built_forms and \ + (not client_built_form_names or getattr(built_form, 'name', None) in client_built_form_names) + built_forms_for_set = filter(client_filter, built_forms_for_set) + + built_form_set.built_forms.add(*built_forms_for_set) + return built_form_set + + +def on_config_entity_pre_delete_built_form(sender, **kwargs): + pass diff --git a/footprint/main/publishing/config_entity_publishing.py b/footprint/main/publishing/config_entity_publishing.py new file mode 100644 index 000000000..15f2f57f9 --- /dev/null +++ b/footprint/main/publishing/config_entity_publishing.py @@ -0,0 +1,198 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +import logging +from django.contrib.auth.models import User +from django.db.models.signals import pre_save, post_save +from django.dispatch import Signal +# from memory_profiler import profile +from footprint.main.models.config.global_config import GlobalConfig +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.publishing.publishing import post_save_publishing +from footprint.main.models.config.scenario import FutureScenario, BaseScenario +from footprint.main.models.config.project import Project +from footprint.main.models.config.region import Region +from footprint.main.publishing import tilestache_publishing, policy_publishing +from footprint.main.publishing import result_publishing +from footprint.main.publishing import db_entity_publishing +from footprint.main.publishing import data_import_publishing +from footprint.main.publishing import analysis_module_publishing +from footprint.main.publishing import layer_publishing +from footprint.main.publishing import built_form_publishing +from footprint.main.utils.subclasses import receiver_subclasses +from footprint.main.utils.utils import resolvable_module_attr_path + +logger = logging.getLogger(__name__) + +# Signal for all initial publishers. They can run without dependencies +post_save_config_entity_initial = Signal(providing_args=[]) +# Signal for all publishers after built_forms are processed +post_save_config_entity_built_forms = Signal(providing_args=[]) +# Signal for all publishers that can run after db_entities are processed +post_save_config_entity_db_entities = Signal(providing_args=[]) +# Signal for all publishers that can run after layers are processed +post_save_config_entity_layers = Signal(providing_args=[]) +# Signal for all publishers that can run after data importing +post_save_config_entity_imports = Signal(providing_args=[]) +# Signal for all publishers that should run after analytic modules run +post_save_config_entity_analytic_run = Signal(providing_args=[]) + +def post_save_config_entity_initial_publishers(cls): + """ + DbEntity publishing, Analysis Module publishing, and BuiltForm publishing can happen in parallel as soon + as a config_entity is saved + """ + post_save_config_entity_initial.connect(analysis_module_publishing.on_config_entity_post_save_analysis_modules, cls, True, "analysis_module_on_config_entity_post_save") + + post_save_config_entity_initial.connect(built_form_publishing.on_config_entity_post_save_built_form, cls, True, "built_form_publishing_on_config_entity_post_save") + + post_save_config_entity_initial.connect(policy_publishing.on_config_entity_post_save_policy, cls, True, "policy_publishing_on_config_entity_post_save") + +def post_save_config_entity_built_form_publishers(cls): + """ + DBEntity publishing can happen after built_forms + """ + post_save_config_entity_built_forms.connect(db_entity_publishing.on_config_entity_post_save_db_entity, cls, True, "db_entity_on_config_entity_post_save") + +def post_save_config_entity_db_entities_publishers(cls): + """ + Data Import publishing, Layer publishing, and Result publishing can happen after DbEntity publishing + """ + post_save_config_entity_db_entities.connect(data_import_publishing.on_config_entity_post_save_data_import, cls, True, "data_import_on_config_entity_post_save") + + post_save_config_entity_db_entities.connect(layer_publishing.on_config_entity_post_save_layer, cls, True, "layer_on_config_entity_post_save") + +def post_save_config_entity_import_publishers(cls): + """ + Result publishing can run after Data Import publishing + """ + post_save_config_entity_imports.connect(result_publishing.on_config_entity_post_save_result, cls, True, "result_on_config_entity_post_save") + +def post_save_config_entity_layers_publishers(cls): + """ + Tilestache publishing can run after the Layer publisher + """ + post_save_config_entity_layers.connect(tilestache_publishing.on_config_entity_post_save_tilestache, cls, True, "tilestache_on_config_entity_post_save") + +def post_save_config_entity_analytic_runs_publishers(cls): + """ + Tilestache also runs after analytic runs to clear the cache + TODO this should be refined. + """ + post_save_config_entity_analytic_run.connect(tilestache_publishing.on_post_analytic_run_tilestache, cls, True, "tilestache_on_post_analytic_run") + +# Register receivers for only the lineage classes of Scenario subclasses +for cls in [FutureScenario, BaseScenario, Project, Region, GlobalConfig]: + post_save_config_entity_initial_publishers(cls) + post_save_config_entity_built_form_publishers(cls) + post_save_config_entity_db_entities_publishers(cls) + post_save_config_entity_import_publishers(cls) + post_save_config_entity_layers_publishers(cls) + post_save_config_entity_analytic_runs_publishers(cls) + +def dependent_signal_paths(signal_path): + """ + Gives the hierarchy of publisher signal calling order based on the given signal + Signals are given as strings instead of paths for serialization ease + param: signal_path. The signal path for which the dependent signals are returned + return: An array of signal_paths or an empty array + """ + + if signal_path == resolvable_module_attr_path(__name__, 'post_save_config_entity_initial'): + # BuiltForm dependent publishers can run after initial + return [resolvable_module_attr_path(__name__, 'post_save_config_entity_built_forms')] + elif signal_path == resolvable_module_attr_path(__name__, 'post_save_config_entity_built_forms'): + # DbEntity dependent publishers can run after the built_form publishers + return [resolvable_module_attr_path(__name__, 'post_save_config_entity_db_entities')] + elif signal_path == resolvable_module_attr_path(__name__, 'post_save_config_entity_db_entities'): + # Layer and DataImport dependent publishers are run after DbEntity dependent publishers + return [resolvable_module_attr_path(__name__, 'post_save_config_entity_layers'), + resolvable_module_attr_path(__name__, 'post_save_config_entity_imports')] + return [] + +# Very wild guess about config_entity saving proportional times to send to the client +# These represent the parsed signal names sent to the client after the dependencies of +# the signal finish running +signal_proportion_lookup = dict( + # Initial signals complete + post_save_config_entity_initial=.20, + # built_form dependants run after initial + post_save_config_entity_built_forms=.20, + # These run after built_forms + post_save_config_entity_db_entities=.20, + # layers and dataImports run in parallel after dbEntities + post_save_config_entity_layers=.20, + post_save_config_entity_imports=.20 +) + +@receiver_subclasses(pre_save, ConfigEntity, "config_entity_pre_save") +def on_config_entity_pre_save(sender, **kwargs): + """ + A presave event handler. Currently this just defaults the bounds of the instance to those of its parent + :param sender: + :param kwargs: + :return: + """ + instance = kwargs['instance'] + if not instance.pk: + # Inherit the parent's bounds if none are defined + if not instance.bounds: + instance.bounds = instance.parent_config_entity.bounds + +@receiver_subclasses(post_save, ConfigEntity, "config_entity_post_save") +#@profile +def on_config_entity_post_save(sender, **kwargs): + """ + Create the ConfigEntity's database schema on initial save. + Post save starts a chain of asynchronous publishers that run according to a dependency tree. + First publishers that are wired to the post_save_config_entity_initial signal + run, followed by publishers dependent on signals that are dependent of + post_save_config_entity_initial (see dependent_signal_paths) + :param sender: + :param kwargs: + :return: + """ + config_entity = kwargs['instance'] + + for child_config_entity in config_entity.children(): + # Do any needed syncing of config_entity_children + # This currently does nothing + child_config_entity.parent_config_entity_saved() + + # Send a message to publishers to configure after creation or update of the config_entity + # This is executed through a Celery task so that it can run asynchronously + if config_entity._no_post_save_publishing: + return + if config_entity.deleted: + # Also do nothing if the config_entity is deleted. At some point this should do some + # processings, such as rekeying the scenario so it doesn't conflict with new scenario keys + return + + if kwargs.get('created', None) and config_entity.origin_config_entity: + config_entity.add_categories(*config_entity.origin_config_entity.categories.all()) + + # TODO The default user here should be the admin, and in fact all config_entity instances + # should simply have to have a creator + user = config_entity.creator if config_entity.creator else User.objects.all()[0] + starting_signal_path = resolvable_module_attr_path(__name__, 'post_save_config_entity_initial') + + logger.debug("Handler: post_save_config_entity for config_entity {config_entity} and user {username}".format( + config_entity=config_entity, + username=user.username)) + + return post_save_publishing( + starting_signal_path, + config_entity, + user, + instance=config_entity, + signal_proportion_lookup=signal_proportion_lookup, + dependent_signal_paths=dependent_signal_paths, + signal_prefix='post_save_config_entity') diff --git a/footprint/main/publishing/data_export_publishing.py b/footprint/main/publishing/data_export_publishing.py new file mode 100644 index 000000000..c85ff4a87 --- /dev/null +++ b/footprint/main/publishing/data_export_publishing.py @@ -0,0 +1,162 @@ +import logging +import os +import shlex +import shutil +import subprocess +import traceback + +from django.http import HttpResponse, HttpResponseNotFound, HttpResponseForbidden +from django.utils import timezone + +from sendfile import sendfile +import sys +from tastypie.models import ApiKey +from footprint.celery import app +from footprint.common.utils.async import start_and_track_task +from footprint.common.utils.websockets import send_message_to_client +from footprint.common.utils.zip_geodatabase import zip_file_gdb + + +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.layer import Layer +from footprint.main.models.tasks.async_job import Job +from footprint.main.utils.utils import timestamp, database_connection_string_for_ogr +from footprint import settings + +__author__ = 'calthorpe_associates' +logger = logging.getLogger(__name__) + + +SUPPORTED_FORMATS = { + "geojson": '''"GeoJSON"''', + "gdb": '''"FileGDB"''', + "shapefile": '''"ESRI Shapefile"''' +} + + +def export_layer(request, layer_id, api_key): + job = start_and_track_task(_export_layer, api_key, layer_id) + return HttpResponse(job.hashid) + + +@app.task +def _export_layer(job, layer_id): + + try: + layer = Layer.objects.get(id=layer_id) + job.status = "Exporting" + job.save() + + db_entity = layer.db_entity_interest.db_entity + + export_file, filename = export_db_entity_to_file(db_entity) + + job.status = "Zipping" + job.save() + + zip_file_gdb(export_file) + shutil.rmtree(export_file) + + job.data = '/' + filename + ".zip" + job.save() + + send_message_to_client(job.user.id, dict(event='layerExportCompleted', + layer_id=layer_id, + job_id=str(job.hashid))) + + job.status = 'Complete' + + except Exception, e: + job.status = "Failed" + + exc_type, exc_value, exc_traceback = sys.exc_info() + readable_exception = traceback.format_exception(exc_type, exc_value, exc_traceback) + job.data = readable_exception + job.save() + + send_message_to_client(job.user.id, dict(event=job.type + " failed", trace=readable_exception)) + + print job.status, job.data + job.ended_on = timezone.now() + job.save() +# +# return tracked_job +def get_export_result(request, api_key, hash_id): + job = Job.objects.get(hashid=hash_id) + + try: + user_id = ApiKey.objects.get(key=api_key).user_id + assert user_id == job.user.id + except: + return HttpResponseForbidden("This user did not request that file!") + + if job.status != "Complete": + return HttpResponseNotFound("Export is not complete") + filepath = settings.SENDFILE_ROOT + job.data + print "trying to send " + filepath + return sendfile(request, filepath) + + +def export_db_entity_to_file(db_entity, export_file=None, export_format="gdb", fields=None): + + if fields: + field_string = '' + for field in fields: + field_string += field + ', ' + field_string = field_string[:-2] + else: + field_string = " * " + + table = "{schema}.{table}".format(**db_entity.__dict__) + + select_statement = "select * from (select {fields} from {table}) as {feature_class};".format( + fields=field_string, table=table, feature_class=db_entity.key) + filename = construct_export_filename(db_entity, export_format) + + if not export_file: + export_file = "{SENDFILE_ROOT}/{filename}".format( + SENDFILE_ROOT=settings.SENDFILE_ROOT, + filename=filename) + + print "attempting to create" + export_file + + export_command = generate_ogr_command(export_format, select_statement, export_file) + export_command_args = shlex.split(export_command) + print export_command, export_command_args + # shlex ("simple lexical analysis") splits the command string into its arguments before it runs in subprocess + ogr_result = os.system(export_command) #, shell=True) + # print result + if ogr_result: + raise Exception(ogr_result) + logger.info("file ready for download") + + return export_file, filename + + +def generate_ogr_command(export_format, select_statement, export_file): + ogr_command = "/usr/local/bin/ogr2ogr -append -f {ogr_format} -sql \'{select_statement}\' -nlt MULTIPOLYGON {export_file} "\ + "PG:\"{db_connection}\" {options}" + logger.debug("Data Export: " + ogr_command) + + options = "FGDB_BULK_LOAD" if export_format == 'gdb' else '' + + return ogr_command.format( + ogr_format=SUPPORTED_FORMATS[export_format], + srs=Keys.SRS_4326, + select_statement=select_statement, + export_file=export_file, + db_connection=database_connection_string_for_ogr('default'), + options=options + ) + + +def construct_export_filename(entity, extension): + export_file_name = "{db_entity}_{timestamp}.{extension}".format( + db_entity=entity.name.replace(" ", "_"), + timestamp=timestamp(), + extension=extension) + + return export_file_name + + + diff --git a/footprint/main/publishing/data_import_publishing.py b/footprint/main/publishing/data_import_publishing.py new file mode 100644 index 000000000..461c763ab --- /dev/null +++ b/footprint/main/publishing/data_import_publishing.py @@ -0,0 +1,842 @@ +#from memory_profiler import profile +import logging +import re +import traceback +from django.contrib.gis.db import models +from django.db.models.fields.related import ReverseManyRelatedObjectsDescriptor +from django.db import connections, connection, transaction +from south.utils.datetime_utils import datetime +import sys +from footprint import settings +from footprint.common.utils.postgres_utils import build_postgres_conn_string, pg_connection_parameters +from footprint.main.database.import_data import ImportData +from footprint.main.lib.functions import map_property, merge, map_dict, map_to_dict, filter_dict, map_dict_to_dict, deep_merge + +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.models.config.project import Project + +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.main.publishing.import_processor import ImportProcessor +from footprint.main.utils.dynamic_subclassing import create_tables_for_dynamic_classes, drop_tables_for_dynamic_classes, resolve_field, create_join, ManyJoinRelationship, SingleJoinRelationship +from footprint.main.utils.utils import parse_schema_and_table, get_property_path, resolve_module_attr +from footprint.main.models.database.information_schema import InformationSchema, sync_geometry_columns +from footprint.uf_tools import dictfetchall +import psycopg2 + +__author__ = 'calthorpe_associates' + +logger = logging.getLogger(__name__) +UF_GEOMETRY_ID = 'uf_geometry_id' +UF_GEOMETRY_ID_TYPE = 'varchar' + +#@profile +def on_config_entity_post_save_data_import(sender, **kwargs): + """ + Import the data for all DbEntities (if needed) of the ConfigEntity + :param sender: + :param kwargs: 'instance': The config_entity, 'db_entity_keys': Optional. Limits the DbEntities processed to the + given keys + :return: None + """ + config_entity = kwargs['instance'] + logger.debug("\t\tHandler: on_config_entity_post_save_data_import. ConfigEntity: %s" % config_entity.name) + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + + process_db_entities(**kwargs) + +#@profile +def on_db_entity_post_save_data_import(sender, **kwargs): + """ + On DbEntity save import its data + :param sender: + :param kwargs: 'instance': The DbEntityInterest + :return: None + """ + + db_entity_interest = kwargs['instance'] + config_entity = db_entity_interest.config_entity + db_entity = db_entity_interest.db_entity + logger.debug("\t\tHandler: on_db_entity_post_save_data_import. DbEntity: %s" % db_entity.full_name) + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + + process_db_entity( + config_entity, + db_entity, + **kwargs) + + +def process_db_entities(**kwargs): + """ + process all of the db_entities or a limited number for the given config_entity + :param kwargs: 'instance' is the config_entity, 'db_entity_keys' is an optional list of + keys to limit the DbEntities. 'importer_processor' overrides the default ImportProcessor with a custom class + used on each DbEntity + """ + config_entity = kwargs['instance'] + # Get the list of DbEntity instances and their corresponding subclasses (the latter if it is configured) + # Make sure these are both owned by the config_entity and not clones + # Only import the DbEntity if the config_entity is the owner of the db_entity. + # The ones that adopt it need not re-import + limited_db_entity_keys = kwargs.get('db_entity_keys', None) + + db_entities = filter(lambda db_entity: (not limited_db_entity_keys or db_entity.key in limited_db_entity_keys) and not db_entity.is_clone, config_entity.owned_db_entities()) + for db_entity in sorted( + db_entities, + key=lambda db_entity: 0 if \ + db_entity.feature_class_configuration and db_entity.feature_class_configuration.get('primary_geography') else \ + 1): + # TODO this filter is just here to block remote urls of background layers + # It doesn't really make much sense otherwise, although we do have to import from somewhere + # It certainly doesn't make sense in the clone case put clones copy their source's urls + if db_entity.importable: + process_db_entity( + config_entity, + db_entity, + **kwargs) + +def process_db_entity(config_entity, db_entity, **kwargs): + """ + Processes a db_entity according to its feature_class_configuration. + An ImportProcessor is chosen for the db_entity based on its feature_class_configuration.data_importer + configuration, or it defaults to DefaultImporter. The kwargs of this importer's constructor + optionally come from feature_class_configuration.data_importer_kwargs + The ImporterProcessor does one of three operations: importer, peer_importer, or cloner. + Importer imports primary data from a source peer_importer imports based on a peer DbEntity in + the config_entity. cloner clones from the same db_entity of another config_entity. + The operation chosen is based on the feature_class_configuration + :param config_entity: The db_entity owner + :param db_entity: The instance whose features are being importer + :param kwargs: specify 'import_processor' to specify a custom ImportProcessor for all db_entities + to override it for the db_entity with a custom class + """ + + + custom_import_processor = db_entity.feature_class_configuration.get('data_importer', None) if db_entity.feature_class_configuration else None + import_processor = kwargs.get('import_processor', + (resolve_module_attr(custom_import_processor) if \ + custom_import_processor else \ + DefaultImportProcessor))(db_entity=db_entity) + + logger.debug("\t\t\tData Import Publishing. Processor: %s DbEntity: %s" % ( + import_processor.__class__.__name__, db_entity.full_name)) + + if config_entity.origin_config_entity and FeatureClassCreator(config_entity, db_entity).feature_class_is_ready: + # If there is an origin_config_entity we override our importing and clone from the origin if this isn't a newly imported layer + # feature_class_is_ready indicates that tit has already been imported + # The db_entity configured to import from another db_entity's class instance objects + logger.debug("\t\t\t\tCloning DbEntity: %s" % db_entity.full_name) + import_processor.cloner(config_entity, db_entity) + elif db_entity.feature_class_configuration and db_entity.feature_class_configuration.get('import_from_db_entity_key', None): + # Copy data from a peer table + # The db_entity configured to import from another db_entity's class instance objects + logger.debug("\t\t\t\tPeer Importing DbEntity: %s" % db_entity.full_name) + import_processor.peer_importer(config_entity, db_entity) + elif db_entity.importable: + # Import from a source table + # The table data needs to be imported from a seed table in the public schema + logger.debug("\t\t\t\tSource Importing DbEntity: %s" % db_entity.full_name) + import_processor.importer(config_entity, db_entity) + + +def on_db_entity_save(sender, **kwargs): + """ + respond to whenever a db entity is added or updated + :return: + """ + db_entity = kwargs['instance'] + if not db_entity._no_post_post_save: + # Do updates here + pass + + +class DefaultImportProcessor(ImportProcessor): + + def importer(self, config_entity, db_entity): + """ + Imports a feature table from a remote server + :param config_entity: + :param db_entity: + :return: + """ + + # Import from a sql dump and create Geography instances for each feature in anticipation of importing the feature + # Get the name of the source db_entity table, which might be modified for testing + + if not InformationSchema.objects.table_exists(db_entity.schema, db_entity.table): + # Attempt to fetch the feature tables from the import database. This dumps the tables to the local server + # and pipes them into the config_entity's schema. + ImportData(config_entity=config_entity, db_entity_key=db_entity.key).run() + add_primary_key_if_needed(db_entity) + + # Once the db_entity feature data is present in the system, get or create the feature_class_configuration and + # pouplate association tables + # Use the feature_class configuration to create the feature_class. Otherwise inspect the imported table to create it + if db_entity.feature_class_configuration and not db_entity.feature_class_configuration.get('generated', False): + # Nothing to do + pass + else: + # Create the feature_class_configuration using introspection on the table and assign it to the db_entity + feature_class_creator = FeatureClassCreator(config_entity, db_entity) + # Find fields by introspecting the imported table + feature_class_configuration = feature_class_creator.feature_class_configuration_from_introspection() + # Add these fields to the feature_class_configuration + feature_class_creator.merge_feature_class_configuration_into_db_entity(feature_class_configuration) + + # Create association classes and tables and populate them with data + create_and_population_associations(config_entity, db_entity) + + def peer_importer(self, config_entity, db_entity, import_from_origin=False, source_queryset=None): + """ + Creates the ConfigEntity specific FeatureClass table by importing from a peer table, the former is + indicated by the feature_class and the latter by the source_feature_class. + :param config_entity: The config_entity import target + :param db_entity: The db_entity target + :param import_from_origin: Optionally set True to import from the origin_instance of the DbEntity. + :param source_queryset: Optionally limit the features imported, or even expand what is import to a joined queryset. + The latter would have to be done in conjunction with import_fields (and isn't implemented.) + By default all fields modeled by the feature_class are imported + :return: + """ + source_db_entity_key = db_entity.origin_instance.key if \ + import_from_origin else \ + db_entity.feature_class_configuration.get('import_from_db_entity_key') + # Custom import field names. Normally not needed + import_fields = db_entity.feature_class_configuration.get('import_fields', []) + source_db_entity = config_entity.computed_db_entities().get(key=source_db_entity_key) + source_feature_class = config_entity.feature_class_of_db_entity_key(source_db_entity_key) + feature_class = config_entity.feature_class_of_db_entity_key(db_entity.key) + if not InformationSchema.objects.table_exists(db_entity.schema, db_entity.table): + # Create the feature_class table and its base class if they don't yet exist + create_tables_for_dynamic_classes(feature_class.__base__, feature_class) + + # Import the data from the source feature class + _peer_or_clone_table_import(feature_class, source_feature_class, + source_queryset=source_queryset, + import_fields=import_fields, + import_ids_only=db_entity.feature_class_configuration.get('import_ids_only', None)) + # Add and fill the association tables based on the origin db_entity + create_and_population_associations_from_clone_source(config_entity, db_entity, source_db_entity=source_db_entity, source_queryset=source_queryset) + + def cloner(self, config_entity, db_entity): + """ + Clones data from the config_entity's origin_config_entity. We only do this if the db_entity + is owned by the config_entity. In other words if we're a Scenario don't copy Project or Region + scoped db_entities. + :param config_entity: + :param db_entity_key: + :param import_fields. Optional limited fields to import. If None it imports all fields. + :return: + """ + + if config_entity.db_entity_owner(db_entity) != config_entity: + return + + destination_feature_class = config_entity.feature_class_of_db_entity_key(db_entity.key) + source_feature_class = config_entity.origin_config_entity.feature_class_of_db_entity_key(db_entity.key) + # Custom import field names. Normally not needed + import_fields = db_entity.feature_class_configuration.get('import_fields', []) + + if not InformationSchema.objects.table_exists(db_entity.schema, db_entity.table): + # Create the destination_feature_class table and its base class if they don't yet exist + create_tables_for_dynamic_classes(destination_feature_class.__base__, destination_feature_class) + + # Copy the data from the source_feature_class + _peer_or_clone_table_import(destination_feature_class, source_feature_class, import_fields) + # Add and fill the association tables based on the origin db_entity + source_db_entity = config_entity.origin_config_entity.computed_db_entities().get(key=db_entity.key) + create_and_population_associations_from_clone_source(config_entity, db_entity, source_db_entity=source_db_entity) + +def add_primary_key_if_needed(db_entity): + """ + If an imported table came in without a primary key column, add it. If it came in with + a different primary_key than our preferred one, rename it + """ + + primary_key = db_entity.feature_class_configuration.get('primary_key', 'id') + InformationSchema.add_column_conditionally( + db_entity.schema, + db_entity.table, + primary_key, + db_entity.feature_class_configuration.get('primary_key_type', 'integer'), + primary_key=True, + create_primary_key_duplicate_column='id' if db_entity.feature_class_configuration.get('primary_key', + None) else None) +def create_and_populate_geography_associations(db_entity, feature_class): + # By default create geography associations + geography_class = FeatureClassCreator(feature_class.config_entity, db_entity).dynamic_geography_class() + # Create the geography class table if not already created + create_tables_for_dynamic_classes(geography_class) + if db_entity.feature_class_configuration.get('primary_geography', None): + # If the table contains primary geographies, get or create a dynamic subclass of + # Geography to fill the table with the geographies. + # We use the source_table_id column of the Geography table to guarantee uniqueness of the rows, + # so that they won't be imported twice. + update_or_create_primary_geographies(db_entity, feature_class) + # Since we're joining on the feature class relations table, use the parent field for the source table id + # to uniquely identify each row along with source_table_id + if db_entity.feature_class_configuration.get('primary_geography', None) or \ + get_property_path(db_entity.feature_class_configuration, 'intersection.type') == 'attribute': + # For classes whose primary key MATCHES a primary_geography feature class + + source_db_entity = db_entity if db_entity.feature_class_configuration.get('primary_geography', None) else \ + feature_class.config_entity.computed_db_entities(key=get_property_path(db_entity.feature_class_configuration, 'intersection.db_entity_key'))[0] + + parent_field = feature_class._meta.parents.values()[0] + related_field_configuration = dict( + source_class_join_field_name=parent_field.name, + related_class_join_field_name='source_id', + ) + populate_many_relation(feature_class, 'geographies', related_field_configuration, + extra=(dict( + where=['{geography_class_name}.source_table_id = {source_table_id}'.format( + geography_class_name=geography_class._meta.db_table, + source_table_id=source_db_entity.id)])), + ) + else: + related_field_configuration = dict( + source_class_join_field_name='wkb_geometry', + related_class_join_field_name='geometry' + ) + # Perform the intersection based on the configuration. The intersection dict has type and to keys + # Type is the type of intersection for the main class and to holds the type of intersection for the primary + # geographies to which where joining. Either can be point or centroid + intersection = db_entity.feature_class_configuration.get('intersection', None) + if not intersection: + raise Exception("Expected key intersection not configured for feature_class_configuration of db_entity %s" % db_entity) + intersection_types = dict(centroid='ST_Centroid({0})', polygon='{0}') + custom_join = r'ON ST_Intersects({frm}, {to})'.format( + frm=intersection_types[intersection['type']].format('\g<1>'), + to=intersection_types[intersection.get('to', 'polygon')].format('\g<2>') + ) + + populate_many_relation(feature_class, 'geographies', related_field_configuration, + custom_join=custom_join, + join_on_base=True) + + +def create_and_population_associations(config_entity, db_entity): + """ + Creates all association classes and tables and populates the tables according to the DbEntity feature_class_configuration + :param db_entity: The DbEntity whose feature_class associations are to be populated + :return: + """ + feature_class = FeatureClassCreator(config_entity, db_entity).dynamic_feature_class() + # Create the feature_class relation table if it doesn't exist. The base table will be created by the import process + create_tables_for_dynamic_classes(feature_class) + + # For ForeignKey relationships, the feature_class table holding the relationships is populated + # This populates the feature_class's rel table no matter what so that it can be used below + single_related_field_configurations = filter_dict( + lambda related_field_name, related_field_configuration: related_field_configuration['single'], + db_entity.feature_class_configuration.get('related_fields', {})) + populate_single_relations(feature_class, single_related_field_configurations) + + # Populate the Geography table of the config_entity schema for db_entities marked feature_class_configuration.primary_geography + # Also create and populate the Feature to Geography through classes and tables. These through tables will associate with any + # rows in the Geogrpahy table that they match by intersection (or by attribute match if the through class associates a primary geogrpahy DbEntity) + create_and_populate_geography_associations(db_entity, feature_class) + + # Each field_name in the mapping indicates a mapping from the associated class to the local class + for relation_field_name, related_field_configuration in filter_dict( + lambda related_field_name, related_field_configuration: not related_field_configuration['single'], + db_entity.feature_class_configuration.get('related_fields', {})): + # Each field_name in the mapping indicates a mapping from the associated class to the local class + # For ForeignKey relationships, the feature_class table holding the relationships is populated + populate_many_relation(feature_class, + relation_field_name, + related_field_configuration, + # Join the related class on its base table if its a related_db_entity + # since only the base table has the field we want to join + join_related_on_base=related_field_configuration.get('related_db_entity', False) and True) + + +def populate_single_relations(feature_class, related_field_configurations, query=None, filter_dict=None, extra=None, custom_join=None): + """ + Populates all ForeignKey fields in the feature_class relationships table. + Even if there are no ForeignKey fields this will populate the table with just the parent table pk so that it can be used for querying + :param feature_class: + :param related_field_configurations: + :param query: + :param filter_dict: + :param extra: + :param custom_join: + :return: + """ + # Quit if the feature_class relationships table is already populated + if feature_class.objects.count() > 0: + return + + # Create each SingleJoinRelationship + relationships = map_dict_to_dict( + lambda related_field_name, related_field_configuration: [ + related_field_name, + SingleJoinRelationship(feature_class, related_field_name, related_field_configuration, + query=None, filter_dict=None, extra=None, custom_join=None, + # Join on the base class of the related class if its a feature relationship + join_related_on_base=related_field_configuration.get('related_db_entity_key', False) and True)], + related_field_configurations) + + parent_field = feature_class._meta.parents.values()[0] + + def get_mapped_related_results(related_field_name, relationship): + # Create a single dict for the results of each relationship. This will be in the form + # {pk1: {related_field_name:value}, pk2: {related_field_name: value}, ...} + related_field_column = resolve_field(relationship.source_class, related_field_name).column + return map_to_dict( + # Take each result dict {pk:n, related_pk:m} and map it to a 2-level dict with unique inner key that we can merge + lambda result: + [result['id'], {related_field_column:result['related_pk'], parent_field.column:result['id']}], + # Use the base version of the feature_class. The rel table is what we're filling + get_related_results(feature_class.__base__, relationship)) + + def get_all_base_results(): + # make sure to merge in all base object values in case there are no relationships or null foreign keys that cause joins to be missing + return map_to_dict( + # Take each result dict {pk:n, related_pk:m} and map it to a 2-level dict with unique inner key that we can merge + lambda result: [result['id'], {parent_field.column:result['id']}], + # Use the base version of the feature_class. The rel table is what we're filling + feature_class.__base__.objects.values('id')) + + # Get the results of each relationship and create a lookup table by coalescing the results based on pk + merged_results = deep_merge( + # Get all the results from the base so we have every record + # Merge the relationship results. This will create the form + # {pk1: {related_field_name1:value, related_field_name2: value, ...}, pk2: ...} + *[get_all_base_results()] + map_dict( + get_mapped_related_results, + relationships) + ).values() + + if merged_results: + # Do a manual SQL insert. Django doesn't support bulk inserts on child models, plus the parent table is already full of the records, + # so inserting via Django doesn't make sense + columns = [parent_field.column] + map(lambda relationship: relationship.related_field.column, relationships.values()) + column_names = ', '.join(columns) + column_values = ', '.join(map(lambda result: + '(' +', '.join(map( + lambda column: str(result.get(column, 'null')), + columns)) + + ')', + merged_results)) + cmd = "INSERT INTO {table} ({column_names}) VALUES {column_values}".format( + table=feature_class._meta.db_table, column_names=column_names, column_values=column_values + ) + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + result = cursor.execute(cmd) + if result: + raise result + + +def populate_many_relation(feature_class, related_field_name, related_field_configuration, + query=None, filter_dict=None, extra=None, custom_join=None, + join_on_base=False, join_related_on_base=False): + """ + Populate an association table that is associated with the given feature_class. + You must specify source_field_name and field_name_on_related_class to join on those two values, or specify a query to skip the join process + :param feature_class: + :param related_field_name: The field name of the feature_class that leads to the associated class. + This doesn't have to be a real column on the table, but it must be modeled as a ForeignKey, ManyToMany, etc + :param related_field_configuration. Configuration dict containing: + source_class_join_field_name: Optional The name of the feature_class field whose value will be used to join with the associated class table + related_class_join_field_name: Optional The field name of the feature_class that points to the association + :param query: Optional query that contains the data fro bulk insert + :param filter_dict: Optional filter for the query + :param extra: For where clauses on the join table + :param custom_join: regex string to replace the normal table1.field1 = table2.field2 join clause. The two table.field segments are captured by a regex and put in the custom_join + regex. So the custom_join must be a regex and include a \1 and \2. + :param join_on_base: Join the feature_class using its base class (not its rel class) + :param join_related_on_base: Join the related_class using its base class (not its rel class) + :return: + """ + + relationship = ManyJoinRelationship(feature_class, related_field_name, related_field_configuration, + query=query, filter_dict=filter_dict, extra=extra, custom_join=custom_join, + join_on_base=join_on_base, join_related_on_base=join_related_on_base) + + # Create the through table if doesn't yet exist. This would only happen for an explicit through class + create_tables_for_dynamic_classes(relationship.through_class) + + # Quit if the table is already populated + if relationship.through_class.objects.count() > 0: + return + + results = get_related_results(feature_class, relationship) + # Formulate a lambda that maps the two through class column names to each result. + result_lambda = lambda result: { + relationship.through_class_self_column_name: result['id'], + relationship.through_class_related_column_name: result['related_pk']} + + # Finally populate the through class table with the pk of the main class and related class + relationship.through_class.objects.bulk_create(map( + lambda result: relationship.through_class(**result_lambda(result)), results) + ) + + +def get_related_results(feature_class, relationship): + if relationship.source_class_join_field_name and relationship.related_class_join_field_name: + # The field name on the main class and related class are specified. Resolve and join. + source_field = resolve_field(feature_class, relationship.source_class_join_field_name) + related_class_field = resolve_field(relationship.related_class, relationship.related_class_join_field_name) + # Create the join. The extra parameter can supply additional select and where options. + # The join will always add the related table's pk column as 'related_pk'. The main table's columns are all + # by default in the query + selections = create_join(feature_class, source_field, relationship.related_class, related_class_field, + join_on_base=relationship.join_on_base, join_related_on_base=relationship.join_related_on_base, extra=relationship.extra) + elif relationship.query: + # Instead of specifying field names, a custom query is passed in with its own joining done + selections = relationship.query + else: + raise Exception( + "Function requires either both source_class_join_field_name and related_class_join_field_name to be specified, or else query \ + [{feature_class}]".format(feature_class=feature_class)) + + # If specified, add a filter with any extra filter options passed in. + filtered_selections = selections.filter(**relationship.filter_dict) if relationship.filter_dict else selections + if relationship.custom_join: + # If we defined a custom join, we replace the join generated by the query. Capture the two table.column parts + # and format the custom join with them. + # TODO it would be much better if the create_join function above accepted an alternate join equation. + query_string = str(filtered_selections.query) + pattern = r'^ON \((.*?) = (.*?)\)' + join_index = [m.start() for m in re.finditer('ON', query_string)][-1] + replacement_query = query_string[:join_index] + re.sub(pattern, relationship.custom_join, query_string[join_index:]) + # Excecute the updated query and put the results in dicts + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + cursor.execute(replacement_query) + results = dictfetchall(cursor) + else: + results = filtered_selections.values() + return results + + +def update_or_create_primary_geographies(db_entity, feature_class): + """ + Create or update the geography rows for the given primary geography db_entity. + :param db_entity + :param feature_class + """ + + # Get or create the Geography class for the config_entity schema + geography_class = FeatureClassCreator(feature_class.config_entity, db_entity).dynamic_geography_class() + geography_table = geography_class._meta.db_table + + # Tell PostGIS about the new geometry column of the dynamic geography table if needed + schema, table = parse_schema_and_table(geography_table) + sync_geometry_columns(schema, table) + + full_table_name = db_entity.full_table_name + logger.debug("Updating/Creating rows of geography table {geography_table} for of schema {schema} with tables {source_table} of db_entity key {db_entity_key}".format( + geography_table=geography_table, + schema=db_entity.schema, + source_table=full_table_name, + db_entity_key=db_entity.key)) + + if not feature_class.objects.count() > 0: + logging.debug("No rows found in primary geography table {full_table_name}".format(full_table_name=full_table_name)) + return + + # If the Geography table already has one of our source_ids assume that we already did the import + if len(geography_class.objects.filter(source_table_id=str(db_entity.id))) > 0: + logging.debug("Geography table {geography_table} already contains a source_id matching the first value of {full_table_name}".format( + geography_table=geography_table, + full_table_name=full_table_name, + )) + return + + # Insert the geography and source_table_id and source_id into the schema's geography table. + # Use the db_entity.id as the source_table_id and the + # This indicates what table provide each row of data + geography_column_insert_sql = "insert into {geography_table} (geometry, source_table_id, source_id) \ + select st_SetSRID(st_transform(wkb_geometry, 4326),4326), {source_table_id}, id from {source_table}".format( + geography_table=geography_table, + source_table=full_table_name, + source_table_id=db_entity.id) + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + result = cursor.execute(geography_column_insert_sql) + + if result: + raise result + +def _peer_or_clone_table_import(destination_feature_class, source_feature_class, source_queryset=None, import_fields=None, import_ids_only=False): + """ + Handles the details of clone_table_import and peer_table_import + A clone table import is used when cloning a ConfigEntity. Each db_entity feature table of the config_entity is copied here + A peer import is used when one feature table of a ConfigEntity is based on another. For example, the future_sceanrio_feature table + is derived from the base_feature table. + + Because Feature classes are modeled with two tables, the "raw" table and the subclass table containing any ForeignKey relationships, + this method fills two tables from their source tables. + :param destination_feature_class: + :param source_feature_class: + :param import_fields: + :param import_ids_only: Default false, copy the rows but only insert the primary key + :return: + """ + + # First fill the base feature class table with that of the source + # The base class represents the "raw" table that doesn't have Footprint created relationships + source_feature_class_base = source_feature_class.__bases__[0] + destination_feature_class_base = destination_feature_class.__bases__[0] + # Resolve the DbEntity of the source to find out if it's a clone + # TODO this might be better for copying uploaded feature tables + _base_or_main_table_import(destination_feature_class_base, + source_feature_class_base, + source_queryset=source_queryset, + import_fields=import_fields, + import_ids_only=import_ids_only) + + # Second fill the rel feature class table, which inherits the base and adds ForeignKey columns + # We don't clone the ManyToMany association tables here + _base_or_main_table_import(destination_feature_class, + source_feature_class, + source_queryset=source_queryset, + import_fields=import_fields, + map_primary_key=True, + import_ids_only=import_ids_only) + +def _base_or_main_table_import(destination_feature_class, source_feature_class, + source_queryset=None, + import_fields=None, + map_primary_key=False, + import_ids_only=False, + simple_import=False): + + import_manager = source_feature_class.objects + import_table = import_manager.model._meta.db_table + + if simple_import: + # If set simple just copy the features directly over + sql = 'truncate table {feature_class_table} cascade; ' \ + 'insert into {feature_class_table} ' \ + 'select * from {source_table};'.format( + feature_class_table=destination_feature_class._meta.db_table, + source_table=import_table) + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + cursor.execute(sql) + return + + + if destination_feature_class.objects.count() == 0: + + # TODO does id belong in here or can we rely on autoincrement? + source_fields = source_feature_class._meta.fields if \ + not import_ids_only else \ + filter(lambda field: field.primary_key or isinstance(field, models.GeometryField), source_feature_class._meta.fields) + source_columns = map_property(filter(lambda field: field.model == source_feature_class, source_fields), 'column') + destination_fields = destination_feature_class._meta.fields if \ + not import_ids_only else \ + filter(lambda field: field.primary_key or isinstance(field, models.GeometryField), destination_feature_class._meta.fields) + destination_columns = map_property(filter(lambda field: field.model == destination_feature_class, destination_fields), 'column') + # Get all the columns of the destination table that are also in the source table + columns = filter(lambda c: c in source_columns, destination_columns) + if import_fields: + if len(columns) > len(import_fields)+1: + # If explicit import_fields were provided that don't exist on the source, raise an error + raise Exception("Some source feature_class, fields in the explicit import_fields array do not exist on the source table. Missing fields %s" % + (source_feature_class, set(columns) - set(source_columns))) + elif len(columns) < len(import_fields)+1: + # If explicit import_fields were provided that don't exist on the destination, raise an error + raise Exception("For destination feature class %s, Some fields in the explicit import_fields array do not exist on the destination table. Missing fields %s" % + (destination_feature_class, set(source_columns) - set(columns+['id']))) + + destination_column_string = ', '.join( + columns + ([destination_feature_class._meta.parents.values()[0].column] if map_primary_key else []) + ) + + # We need the source field names, not columns, since we use Django to select from the source + source_column_to_field_name = map_to_dict(lambda source_field: [source_field.column, source_field.name], source_fields) + source_field_names = map(lambda column: source_column_to_field_name[column], columns) + updated_source_field_names = source_field_names + ([source_feature_class._meta.parents.values()[0].name] if map_primary_key else []) + logger.debug("\t\t\tSource field names: %s" % ', '.join(updated_source_field_names)) + logger.debug("\t\t\tDestination columns: %s" % destination_column_string) + + # Get default values for the columns NOT in columns + default_column_string, column_defaults_string = _get_default_columns_and_values(destination_feature_class, columns, skip_primary_key=map_primary_key) + select_queryset = (source_queryset or source_feature_class.objects).values(*updated_source_field_names).query + select_queryset_with_defaults = str(select_queryset).replace(' FROM', ', {column_defaults_string} FROM'.format(column_defaults_string=column_defaults_string) if column_defaults_string else ' FROM') + + sql = 'insert into {feature_class_table}({destination_column_string} {default_columns_string}) ' \ + '{select_queryset}'.format( + feature_class_table=destination_feature_class._meta.db_table, + destination_column_string=destination_column_string, + select_queryset=select_queryset_with_defaults, + source_table=import_table, + default_columns_string=', %s' % default_column_string if default_column_string else '' + ) + logger.debug("\t\t\tInsert SQL: %s" % sql) + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + cursor = conn.cursor() + cursor.execute(sql) + +def create_and_population_associations_from_clone_source(config_entity, db_entity, source_db_entity=None, source_queryset=None): + """ + Creates all association classes and tables and populates the tables according to the DbEntity feature_class_configuration. + Note that this is only needed for ManyToMany fields, since ForeignKey fields have no association table. + ForeignKey fields are filled during the clone process + :param config_entity + :param db_entity: The DbEntity whose feature_class associations are to be populated + :param source_db_entity: An optional source DbEntity for the case of a peer_import or clone. This will serv + :param source_queryset: Optional queryset of the source_feature_class used to limit the associations created. This defaults + to all features (source_feature_class.objects.all()) + :return: + """ + destination_feature_class_creator = FeatureClassCreator(config_entity, db_entity) + destination_feature_class = destination_feature_class_creator.dynamic_feature_class() + source_feature_class_creator = FeatureClassCreator(config_entity, source_db_entity) + source_feature_class = source_feature_class_creator.dynamic_feature_class() + + # Force modeling of the geography classes if not already + source_feature_class_creator.dynamic_geography_class() + destination_feature_class_creator.dynamic_geography_class() + + # Do a simple copy from the source on the geographies and all many related fields defined on the destination + # Note that no population of ForeignKey fields happens here, since that data is stored in the feature_class rel table + for related_field_name, related_field_configuration in\ + merge( + {'geographies': + dict(source= + dict( + related_class_name= + source_feature_class_creator.dynamic_geography_class_name(), + related_class_join_field_name='wkb_geometry', + source_class_join_field_name='geometry', + ), + destination= + dict( + related_class_name= + destination_feature_class_creator.dynamic_geography_class_name(), + related_class_join_field_name='wkb_geometry', + source_class_join_field_name='geometry', + ), + ) + }, + map_dict_to_dict( + lambda related_field_name, source_related_field_configuration: + [related_field_name, dict(source=source_related_field_configuration, destination=source_related_field_configuration)], + filter_dict( + lambda related_field_name, related_field_configuration: not related_field_configuration.get('single', None), + db_entity.feature_class_configuration.get('related_fields', {}) + ) + ) + ).items(): + + # Contruct a class instance that models the ManyToMany relationship through the related field for the source class + source_relationship = ManyJoinRelationship(source_feature_class, related_field_name, related_field_configuration['source']) + # Fetch the source through instances limited to the features in the destination + source_instances = source_relationship.through_class.objects.filter( + **{'{0}__pk__in'.format(source_relationship.through_class_self_field.name):destination_feature_class.objects.values_list('id', flat=True)}) + # Contruct a class instance that models the ManyToMany relationship through the related field for the destination class + relationship = ManyJoinRelationship(destination_feature_class, related_field_name, related_field_configuration['destination']) + # Create the through class table if not already created + create_tables_for_dynamic_classes(relationship.through_class) + if relationship.through_class.objects.count() > 0: + # Already populated this through class table + continue + # Map the source results to the destination result attribute names + # Take advantage of the fact that the pks on the source_feature_class and destination_feature_class are identical + # TODO that might be problematic + # TODO this does a lookup on every related instance that I'd like to avoid by doing a bulk select first (using an annotation on source_instances above) + feature_lookup = map_to_dict( + lambda feature: [feature.id, feature], + destination_feature_class.objects.all()) + + result_lambda = lambda source_instance: { + relationship.through_class_self_field.name: feature_lookup[getattr(source_instance, source_relationship.through_class_self_column_name)], + relationship.through_class_related_field.name: getattr(source_instance, source_relationship.through_class_related_field.name) + } + relationship.through_class.objects.bulk_create(map( + lambda source_instance: relationship.through_class(**result_lambda(source_instance)), source_instances) + ) + + +def _get_default_columns_and_values(feature_class, columns=[], skip_primary_key=False): + """ + Retrieves the default values for the given columns. + :param feature_class: + :param columns: + :return: Two strings. The first is the list of columns, the second is the defaults of those columns + """ + default_django_fields = filter( + lambda field: field.column not in columns and + field.model == feature_class and + not (skip_primary_key and field.primary_key), + feature_class._meta.fields) + default_columns = '' + column_defaults = '' + for field in default_django_fields: + if field.null: + continue + default_columns += field.column + ", " + if getattr(field, "auto_now", False) or getattr(field, "auto_now_add", False): + column_defaults += "'" + str(datetime.now()) + "', " + else: + column_defaults += "'" + str(field.default) + "', " + + default_column_string = default_columns[:-2] + column_default_string = column_defaults[:-2] + return default_column_string, column_default_string + + +class DeleteImportProcessor(ImportProcessor): + """ + Processes every db_entity equally by dropping its feature and layer_selection table data + """ + def importer(self, config_entity, db_entity): + self.drop_data(config_entity, db_entity) + def peer_importer(self, config_entity, db_entity): + self.drop_data(config_entity, db_entity) + def cloner(self, config_entity, db_entity): + self.drop_data(config_entity, db_entity) + def drop_data(self, config_entity, db_entity): + """ + Drop all feature tables related to the db_entity in order to reimport, remove the db_entity, etc. + :param config_entity: + :param db_entity: + :return: + """ + feature_class_configuration = db_entity.feature_class_configuration + if not feature_class_configuration: + return + + try: + feature_class = FeatureClassCreator(config_entity, db_entity).dynamic_feature_class() + + related_field_through_classes = map_dict( + lambda name, related_descriptor: related_descriptor.through, + filter_dict(lambda name, related_descriptor: + isinstance(related_descriptor, ReverseManyRelatedObjectsDescriptor), + FeatureClassCreator(config_entity, db_entity).related_descriptors())) + db_entity.feature_class_configuration.get('related_fields') + drop_tables_for_dynamic_classes(* + ([FeatureClassCreator(config_entity, db_entity).dynamic_geography_class()] if isinstance(config_entity, Project) else []) + + [feature_class.geographies.through] + + related_field_through_classes) + drop_tables_for_dynamic_classes( + feature_class, + feature_class.__base__ + ) + except Exception, e: + exc_type, exc_value, exc_traceback = sys.exc_info() + readable_exception = traceback.format_exception(exc_type, exc_value, exc_traceback) + logger.warn("Failed to drop class/table: %s" % readable_exception) + +def on_config_entity_pre_delete_data_import(sender, **kwargs): + """ + Delete data for removing or reimporting + :param kwargs: db_entity_keys - optional filter to limit deletes + """ + process_db_entities( + **merge( + dict(import_processor=DeleteImportProcessor), + kwargs)) diff --git a/footprint/main/publishing/db_entity_publishing.py b/footprint/main/publishing/db_entity_publishing.py new file mode 100644 index 000000000..2e2c53166 --- /dev/null +++ b/footprint/main/publishing/db_entity_publishing.py @@ -0,0 +1,374 @@ +import logging +from django.contrib.auth.models import User +from django.db.models.signals import post_save +from django.dispatch import Signal +from footprint.main.models.config.scenario import FutureScenario, BaseScenario +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration, db_entity_configuration_keys +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.main.publishing import data_import_publishing, layer_publishing, result_publishing, tilestache_publishing +from footprint.main.publishing.config_entity_publishing import post_save_publishing +from footprint.main.publishing.geo_json_processor import GeoJsonProcessor +from footprint.main.publishing.origin_db_entity_processor import OriginDbEntityProcessor +from footprint.main.publishing.shapefile_processor import ShapefileProcessor +from footprint.main.utils.subclasses import receiver_subclasses +from footprint.client.configuration.fixture import ConfigEntityFixture + + +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.models.config.global_config import GlobalConfig +from footprint.main.models.config.project import Project +from footprint.main.models.config.region import Region +from footprint.main.models.geospatial.db_entity import DbEntity +from footprint.main.models.database.information_schema import PGNamespace +from footprint.main.lib.functions import filter_keys, merge, remove_keys +from footprint.main.models.config.db_entity_interest import DbEntityInterest +from footprint.main.models.config.interest import Interest +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.utils import resolve_model, resolvable_module_attr_path, full_module_path, resolvable_model_name + +__author__ = 'calthorpe_associates' + +logger = logging.getLogger(__name__) + +# All initial signals. They can run without dependencies +# All signals that can run after db_entities run +post_save_db_entity_initial = Signal(providing_args=[]) +# All signals that can run after layer run +# This name is plural because one can in theory have multiple layers per db_entity +post_save_db_entity_layers = Signal(providing_args=[]) +# All signals that can run after data imports run +post_save_db_entity_import = Signal(providing_args=[]) + +def dependent_signal_paths(signal_path): + """ + Gives the hierarchy of publisher signal calling order based on the given signal + Signals are given as strings instead of paths for serialization ease + param: signal_path. The signal path for which the dependent signals are returned + return: An array of signal_paths or an empty array + """ + if signal_path == resolvable_module_attr_path(__name__, 'post_save_db_entity_initial'): + # DataImport dependent publishers are run after DbEntity dependent publishers + return [resolvable_module_attr_path(__name__, 'post_save_db_entity_import')] + if signal_path == resolvable_module_attr_path(__name__, 'post_save_db_entity_import'): + # To be safe, have Layer dependents import run after data import + return [resolvable_module_attr_path(__name__, 'post_save_db_entity_layers')] + return [] + +# All signals that can run after data imports run +# Very wild guess about config_entity saving proportional times to send to the client +# These represent the parsed signal names sent to the client after the dependencies of +# the signal finish running +signal_proportion_lookup = dict( + # initial signal after save + post_save_db_entity_initial=.34, + # layers and dataImports run in parallel + post_save_db_entity_layers=.33, + post_save_db_entity_import=.33 +) + +def post_save_db_entity_initial_publishers(cls): + """ + Data Import publishing, Layer publishing, and Result publishing can happen after DbEntity publishing + """ + post_save_db_entity_initial.connect(data_import_publishing.on_db_entity_post_save_data_import, cls, True, "data_import_on_db_entity_post_save") + + +def post_save_db_entity_import_publishers(cls): + """ + Layers and Result publishing can run after Data Import publishing + """ + post_save_db_entity_import.connect(layer_publishing.on_db_entity_post_save_layers, cls, True, "layer_on_db_entity_post_save") + post_save_db_entity_import.connect(result_publishing.on_db_entity_post_save_result, cls, True, "result_on_db_entity_post_save") + +def post_save_db_entity_layers_publishers(cls): + """ + Tilestache publishing can run after the Layer publisher + """ + post_save_db_entity_layers.connect(tilestache_publishing.on_db_entity_post_save_tilestache, cls, True, "tilestache_on_db_entity_post_save") + +# Register receivers for only the lineage classes of Scenario subclasses +for cls in [FutureScenario, BaseScenario, Project, Region, GlobalConfig]: + post_save_db_entity_initial_publishers(cls) + post_save_db_entity_import_publishers(cls) + post_save_db_entity_layers_publishers(cls) + +#@profile +def on_config_entity_post_save_db_entity(sender, **kwargs): + """ + Sync a ConfigEntity's DbEntities + """ + config_entity = kwargs['instance'] + logger.debug("\t\tHandler: on_config_entity_post_save_db_entity. ConfigEntity: %s" % config_entity.name) + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + update_or_create_db_entities(config_entity) + +@receiver_subclasses(post_save, DbEntityInterest, "db_entity_interest_post_save") +def on_db_entity_interest_post_save(sender, **kwargs): + """ + Called after a DbEntityInterest saves, but not when a config_entity is running post_save publishers + In other words, this is only called after a direct DbEntityInterest save/update. + This does the same as post_save_config_entity, but starts with the 'post_save_config_entity_db_entities' + signal to do only DbEntity dependent publishing. + """ + db_entity_interest = kwargs['instance'] + if kwargs.get('created', None): + db_entity = db_entity_interest.db_entity + # TODO + # While we test upload, just delete the previous DbEntitys with the same key name + # in the ConfigEntity. + db_entity_interest.config_entity.db_entities.filter(key=db_entity.key).exclude(id=db_entity.id).delete() + + # Make sure the db_entity's schema matches the config_entity's if not set + # TODO we assume that the schema should match the config_entity, rather than + # an ancestor or the config_entity (like the project or a scenario). There + # are many cases where the schema should not be that of the config_entity, so + # we might want to remove this default and force the saver to set it + if not db_entity.schema or not db_entity.table: + db_entity.schema = db_entity.schema or db_entity_interest.config_entity.schema() + # Always base the table name on the key + db_entity.table = db_entity.key + # Update changes with the publishers turned off + publishers_off = db_entity_interest.config_entity._no_post_save_db_entity_interest_publishing + db_entity_interest.config_entity._no_post_save_db_entity_interest_publishing = True + db_entity_interest.db_entity.save() + db_entity_interest.config_entity._no_post_save_db_entity_interest_publishing = publishers_off + + if db_entity_interest.config_entity.deleted: + # Do nothing for deleted config_entities + return + + # Commence the post-save publishing chain for DbEntityInterests, such as the DataImport and Layer publishers + # but only if _no_post_save_db_entity_interest_publishing wasn't set outside this call + + config_entity = ConfigEntity._subclassed_config_entity(db_entity_interest.config_entity) + # TODO The default user should be the admin + user = config_entity.creator if config_entity.creator else User.objects.all()[0] + db_entity = db_entity_interest.db_entity + + if db_entity.deleted: + # Do nothing for deleted db_entities + return + + # Check to see if the DbEntity has a complete feature_class_configuration + feature_class_creator = FeatureClassCreator(config_entity, db_entity) + if not feature_class_creator.feature_class_is_ready: + # Create or further refine the feature_class_configuration + # Merge what exists with generated=True to mark the configuration as generated rather than preconfigured + db_entity.feature_class_configuration = feature_class_configuration = merge(db_entity.feature_class_configuration or {}, dict(generated=True)) + # Choose the correct importer, if any, to set up the feature_class_configuration and features + if db_entity.origin_instance: + # Import from the origin_instance. This could be a full copy or from the current layer selection features + feature_class_configuration['data_importer'] = full_module_path(OriginDbEntityProcessor) + elif '.json' in db_entity.url: + # Import it using the geojson importer + feature_class_configuration['data_importer'] = full_module_path(GeoJsonProcessor) + elif '.zip' in db_entity.url: + feature_class_configuration['data_importer'] = full_module_path(ShapefileProcessor) + else: + # Assume a remote url for background imagery, and thus no nothing + return + db_entity_interest.config_entity._no_post_save_db_entity_interest_publishing = True + db_entity.save() + db_entity_interest.config_entity._no_post_save_db_entity_interest_publishing = False + + # Quit if the publishers were turned off outside this method + if db_entity_interest.config_entity._no_post_save_db_entity_interest_publishing: + return + + starting_signal_path = resolvable_module_attr_path(__name__, 'post_save_db_entity_initial') + + logger.debug("Handler: post_save_db_entity_interest for config_entity {config_entity}, db_entity {db_entity}, and user {username}".format( + config_entity=config_entity, + db_entity=db_entity_interest.db_entity, + username=user.username)) + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + + + return post_save_publishing( + starting_signal_path, + config_entity, + user, + instance=db_entity_interest, + instance_key=db_entity_interest.db_entity.key, + signal_proportion_lookup=signal_proportion_lookup, + dependent_signal_paths=dependent_signal_paths, + signal_prefix='post_save_db_entity' + ) + +def update_or_create_db_entities(config_entity): + """ + Creates or updates the db_entities of the ConfigEntity + :param config_entity + :return: + """ + + # If not present, create the database schema for this ConfigEntity's feature table data + PGNamespace.objects.create_schema(config_entity.schema()) + + client_fixture = ConfigEntityFixture.resolve_config_entity_fixture(config_entity) + + # Process the DbEntities from the origin_config_entity or the db_entity_configuration from the fixtures. + # We only get those scoped (owned) by the class of our config_entity. The scoped above will be adopted automatically + # and need not be created. This means a Scenario creates DbEntities scoped to Scenario and adopts those scoped + # to Project or Region. It does not clone the latter. + + if config_entity.origin_config_entity: + origin_config_entity = config_entity.origin_config_entity + # Clone the DbEntities from the origin ConfigEntity. + # The given kwargs are the only overrides needed to correctly set up the target DbEntity + db_entities_or_configurations = map( + lambda db_entity: clone_or_update_db_entity_and_interest( + config_entity, + db_entity, + schema=config_entity.schema(), + scope=config_entity.id, + geography_scope=FeatureClassCreator(config_entity).resolve_geography_scope(), + class_attrs={'config_entity__id': config_entity.id, 'override_db': config_entity.db, 'db_entity_key': db_entity.key}).db_entity, + origin_config_entity.owned_db_entities()) + else: + # Get the default DbEntity configurations from the fixture + default_db_entity_configurations = client_fixture.default_db_entity_configurations() + # Find additional owned (not adopted) db_entities that aren't defaults, namely those that were created by the user + additional_db_entities = client_fixture.non_default_owned_db_entities() + # Combine the defaults with the additions + db_entities_or_configurations = default_db_entity_configurations+list(additional_db_entities) + + update_or_create_db_entity_interests(config_entity, *db_entities_or_configurations) + # Disable the post_post_save signal while saving to prevent an infinite loop + config_entity._no_post_save_publishing = True + # Save post_create changes. This is just to store selected DbEntities + config_entity.save() + config_entity._no_post_save_publishing = False + +def update_or_create_db_entity_interests(config_entity, *db_entity_configurations_or_db_entities): + """ + Configures saved DbEntities by creating their subclass tables if needed and their DbEntityInterest. This + is an extension of sync_default_db_entities but is also used by publishers to configure the DbEntities + that they need that aren't part of the default sets. + already in a post_config_entity save handler + :param db_entity_configurations_or_db_entities: A list of db_entity configurations or db_entities (the latter + is used if creating a new DbEntity from another) + :return: + """ + + # Getting ready to create or update DbEntityInterests. Tell the DbEntityInterest post_save handler + # to NOT start the DbEntity publishing chain. We don't want to run the DbEntity dependent publishers + # such as Layers and DataImport. The ConfigEntityPublisher will run these itself + config_entity._no_post_save_db_entity_interest_publishing = True + + # Do a forced adoption of DbEntityInterests from the parent ConfigEntity. This makes sure that ConfigEntity has + # the parent's DbEntityInterests before adding any of its own. Otherwise the parent's are never adopted and + # are created from the db_entity_configurations instead, which is minimally less efficient + # See _adopt_from_donor docs for an explanation. + config_entity._adopt_from_donor('db_entities', True) + + db_entity_interests_and_created = map( + lambda db_entity_configuration_or_db_entity: update_or_create_db_entity_and_interest(config_entity, db_entity_configuration_or_db_entity), + db_entity_configurations_or_db_entities) + + # Now add the db_entity_interests that were created. + # They are already associated with the ConfigEntity on creation so this doesn't really do much + created_db_entity_interests = map(lambda tup: tup[0], filter(lambda db_entity_interests_and_created: db_entity_interests_and_created[1], db_entity_interests_and_created)) + config_entity.add_db_entity_interests(*created_db_entity_interests) + + # Remove temporary flag + config_entity._no_post_save_db_entity_interest_publishing = False + +def update_or_create_db_entity_and_interest(config_entity, db_entity_configuration_or_db_entity): + """ + Sync a single db_entity_configuration or db_entity and its db_entity_interest + :return A tuple of the DbEntityInterest and the created flag + """ + if not isinstance(db_entity_configuration_or_db_entity, DbEntity): + # If a subclass of DbEntity is needed, find it here + db_entity_clazz = resolve_model(db_entity_configuration_or_db_entity['db_entity_class_name']) + + # Get or create the feature_class if configured + db_entity = db_entity_clazz.objects.update_or_create(**merge( + # key and schema uniquely identify the DbEntity + filter_keys(db_entity_configuration_or_db_entity, ['key', 'schema']), + dict(defaults=remove_keys(db_entity_configuration_or_db_entity, + ['db_entity_class_name', 'key', 'schema']))))[0] + else: + db_entity = db_entity_configuration_or_db_entity + + logger.debug("\t\t\tConfigEntity/DbEntity Publishing. DbEntity: %s" % db_entity.full_name) + + # Create the DbEntityInterest through class instance which associates the ConfigEntity instance + # to the DbEntity instance. For now the interest attribute is hard-coded to OWNER. This might + # be used in the future to indicate other levels of interest + interest = Interest.objects.get(key=Keys.INTEREST_OWNER) + db_entity_interest, created, updated = DbEntityInterest.objects.update_or_create( + config_entity=config_entity, + db_entity=db_entity, + interest=interest) + + return db_entity_interest, created + + +def full_name_of_db_entity_table(self, table): + """ + :param table: the table name of the table that the DbEntity represents + :return: The combined schema and table name of the given db_entity. This is used to name the dynamic class that is created to represent the table + """ + return '"{0}"."{1}"'.format(self.schema(), table) + +def clone_or_update_db_entity_and_interest(config_entity, source_db_entity, **kwargs): + """ + Clones or updates the source_db_entity modified with the given kwargs (including possibly the key) into this ConfigEntity. + This is used for a duplicate clone from one ConfigEntity (same DbEntity key) to another and also for + creating a modified DbEntity for a Result from a non-Result DbEntity (different DbEntity key). A third case for this + method is cloning a DbEntity within a ConfigEntity, which is not yet implemented. + + If the kwargs['override_on_update'] is True, the kwargs should override the target DbEntity attribute values on update. + This is useful for the Result clone case where we want to pick up updates to the source DbEntity. But in the straight + clone case we want to make the target DbEntity independent of the source once it is created. + Returns the DbEntityInterest + + :param: config_entity. The config_entity of the DbEntities + :param: source_db_entity. The source of the clone + :param: kwargs. Attributes matching the DbEntity that need to override those of the source_db_entity. This might be the + key, schema, etc. override_on_update is optional and is described above + """ + + # key is always resolved by the kwargs or else the source DbEntity key + key = kwargs.get('key', source_db_entity.key) + db_entity_exists = config_entity.db_entities.filter(key=key).count() == 1 + create_or_update_on_override = not db_entity_exists or kwargs.get('override_on_update', False) + + # Prefer the kwargs values over those of the db_entity + db_entity_configuration = merge( + # Extract the db_entity_configuration_keys from the source DbEntity + filter_keys(source_db_entity.__dict__, db_entity_configuration_keys()), + # Override keys with the passed in kwargs only on create or on update if override_on_update==True + remove_keys(kwargs, + list(set(['override_on_update', 'key']+FeatureClassCreator.FEATURE_CLASS_CONFIGURATION_KEYS)-set(['table', 'schema']))) \ + if create_or_update_on_override else dict(), + # Always use this key + dict(key=key), + # Set the clone source key if different than the target key + dict(class_key=source_db_entity.key) if source_db_entity.key != key else dict(), + dict( + # Override feature_class_configuration keys if specified in the kwargs and create_or_update_on_ovrride is True + feature_class_configuration=merge( + source_db_entity.feature_class_configuration, + filter_keys(kwargs, FeatureClassCreator.feature_class_configuration_keys()) if create_or_update_on_override else dict(), + dict(db_entity_key=key))), + dict( + # db_entity_class_name is not stored by the source db_entity, since it knows what class it is + db_entity_class_name=resolvable_model_name(source_db_entity.__class__)) + ) + + # Getting ready to create or update DbEntityInterest. Tell the DbEntityInterest post_save handler + # to NOT start the DbEntity publishing chain. We don't want to run the DbEntity dependent publishers + # such as Layers and DataImport for Result DbEntity. + config_entity._no_post_save_db_entity_interest_publishing = True + + db_entity_interest = update_or_create_db_entity_and_interest(config_entity, db_entity_configuration)[0] + + # Remove temporary flag + config_entity._no_post_save_db_entity_interest_publishing = False + + return db_entity_interest diff --git a/footprint/main/publishing/geo_json_processor.py b/footprint/main/publishing/geo_json_processor.py new file mode 100644 index 000000000..64c42e773 --- /dev/null +++ b/footprint/main/publishing/geo_json_processor.py @@ -0,0 +1,93 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import geojson +from footprint.main.lib.functions import unique, flat_map, map_to_dict +from footprint.main.models.database.information_schema import sync_geometry_columns, InformationSchema +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from django.contrib.gis.geos import GEOSGeometry +from jsonify.templatetags.jsonify import jsonify +from django.conf import settings +from footprint.main.publishing.data_import_publishing import create_and_population_associations +from footprint.main.publishing.import_processor import ImportProcessor +from footprint.main.utils.dynamic_subclassing import create_tables_for_dynamic_classes + +__author__ = 'calthorpe_associates' + +class GeoJsonProcessor(ImportProcessor): + + def __init__(self, **kwargs): + """ + Specify seed_data in kwargs to bypass looking up geojson at the db_entity.url + """ + super(GeoJsonProcessor, self).init() + self.seed_data = kwargs.get('seed_data', None) + + def importer(self, config_entity, db_entity): + """ + Creates various GeojsonFeature classes by importing geojson and saving it to the database via a dynamic subclass of GeojsonFeature + :schema: The optional schema to use for the dynamic subclass's meta db_table attribute, which will allow the class's table to be saved in the specified schema. Defaults to public + :data: Optional python dict data to use instead of loading from the db_entity.url + :return: a list of lists. Each list is a list of features of distinct subclass of GeoJsonFeature that is created dynamically. To persist these features, you must first create the subclass's table in the database using create_table_for_dynamic_class(). You should also register the table as a DbEntity. + """ + if self.seed_data: + data = geojson.loads(jsonify(self.seed_data), object_hook=geojson.GeoJSON.to_instance) + else: + fp = open(db_entity.url.replace('file://', '')) + data = geojson.load(fp, object_hook=geojson.GeoJSON.to_instance) + feature_class_creator = FeatureClassCreator(config_entity, db_entity) + # find all unique properties + feature_class_configuration = feature_class_creator.feature_class_configuration_from_geojson_introspection(data) + feature_class_creator.merge_feature_class_configuration_into_db_entity(feature_class_configuration) + feature_class = feature_class_creator.dynamic_feature_class(base_only=True) + # Create our base table. Normally this is done by the import, but we're just importing into memory + create_tables_for_dynamic_classes(feature_class) + # Now write each feature to our newly created table + for feature in map(lambda feature: self.instantiate_sub_class(feature_class, feature), data.features): + feature.save() + # Create the rel table too + rel_feature_class = feature_class_creator.dynamic_feature_class() + create_tables_for_dynamic_classes(rel_feature_class) + if InformationSchema.objects.table_exists(db_entity.schema, db_entity.table): + # Tell PostGIS about the new geometry column or the table + sync_geometry_columns(db_entity.schema, db_entity.table) + + # Create association classes and tables and populate them with data + create_and_population_associations(config_entity, db_entity) + + def instantiate_sub_class(self, feature_class, feature): + """ + Instantiates an instance of the dynamic subclass of GeoJsonFeature based on the given feature. + :param feature: A feature parsed django-geojson. The feature is actually reserialized to json in order to construct a GEOSGeometry instance. + :return: An instance of the GeoJsonFeature subclass, which contains the geometry, properties of the feature, and perhaps the crs + """ + # TODO, crs should be read from the geojson when present. + # This crs isn't actually picked up by the GEOSGeometry constructor + srid = settings.SRID_PREFIX.format(settings.DEFAULT_SRID) + crs = { + "type": "name", + "properties": { + "name": srid + } + } + # Ironically, we have to rejsonify the data so that GEOSGeometry can parse the feature as json + json = jsonify({'type':feature.geometry.type, 'coordinates':feature.geometry.coordinates, 'crs':crs}) + geometry = GEOSGeometry(json) + field_dict = map_to_dict(lambda field: [field.name, feature.properties[field.name]], + filter(lambda field: feature.properties.get(field.name, None), feature_class._meta.fields)) + return feature_class(wkb_geometry=geometry, **field_dict) + diff --git a/footprint/main/publishing/import_processor.py b/footprint/main/publishing/import_processor.py new file mode 100644 index 000000000..8759c9280 --- /dev/null +++ b/footprint/main/publishing/import_processor.py @@ -0,0 +1,19 @@ +__author__ = 'calthorpe' + +class ImportProcessor(object): + """ + Describes the three methods of import. + importer imports from a source + peer importer imports from a peer DbEntity in the same ConfigEntity + cloner copies from the same DbEntity of another ConfigEntity + """ + def __init__(self, **kwargs): + super(ImportProcessor, self).__init__() + + def importer(self, config_entity, db_entity): + pass + def peer_importer(self, config_entity, db_entity): + pass + def cloner(self, config_entity, db_entity): + pass + diff --git a/footprint/main/publishing/layer_initialization.py b/footprint/main/publishing/layer_initialization.py new file mode 100644 index 000000000..eea21cb37 --- /dev/null +++ b/footprint/main/publishing/layer_initialization.py @@ -0,0 +1,87 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.dispatch import receiver +from footprint.client.configuration.fixture import LayerConfigurationFixture +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.models.tag import Tag +from footprint.main.models.keys.keys import Keys +from footprint.main.models.signals import initialize_media +from footprint.main.utils.utils import create_media_subdir +from footprint import settings + +__author__ = 'calthorpe_associates' + +@receiver(initialize_media) +def initialize_layer_media(sender, **kwargs): + """ + This fires when the application initializes or updates. It creates all the media need by tilestache, namely + style templates and their default contexts + :param sender: + :param kwargs + :return: + """ + + # TODO can we write media to the DB without/instead of the filesystem?? + create_media_subdir('styles') + create_media_subdir('cartocss') + + Tag.objects.update_or_create(tag=LayerTag.BACKGROUND_IMAGERY) + Tag.objects.update_or_create(tag=LayerTag.DEFAULT) + + layer_fixture = resolve_fixture( + "publishing", + "layer", + LayerConfigurationFixture, + settings.CLIENT) + layer_fixture.update_or_create_media() + + + +class LayerLibraryKey(Keys): + class Fab(Keys.Fab): + @classmethod + def prefix(cls): + return 'layer_library' + + # The default layer library + DEFAULT = Fab.ricate('default') + +class LayerKey(Keys): + class Fab(Keys.Fab): + @classmethod + def prefix(cls): + return 'layer' + +class LayerMediumKey(LayerKey): + class Fab(Keys.Fab): + @classmethod + def prefix(cls): + return 'layer_medium' + + # The default medium for all layers + DEFAULT = Fab.ricate('default') + +class LayerTag(Keys): + BACKGROUND_IMAGERY = 'background_imagery' + DEFAULT = 'urbanfootprint_layers' + +class LayerSort(object): + FUTURE = 10 + BASE = 20 + OTHER = 60 + BACKGROUND = 80 diff --git a/footprint/main/publishing/layer_publishing.py b/footprint/main/publishing/layer_publishing.py new file mode 100644 index 000000000..a43467fdb --- /dev/null +++ b/footprint/main/publishing/layer_publishing.py @@ -0,0 +1,352 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +#from memory_profiler import profile +import copy + +import logging +from django.contrib.auth.models import User + +from footprint.client.configuration.fixture import LayerConfigurationFixture, ConfigEntityFixture +from footprint.main.lib.functions import map_to_dict, merge +from footprint.main.models import PresentationConfiguration +from footprint.main.models.presentation.presentation_configuration import LayerConfiguration +from footprint.main.publishing.layer_initialization import LayerMediumKey +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.models.presentation.layer_library import LayerLibrary +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.models.config.scenario import Scenario +from footprint.main.models.presentation.layer_selection import get_or_create_dynamic_layer_selection_class_and_table +from footprint.main.models.presentation.layer import Layer +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.medium import Medium +from footprint.main.models.presentation.template import Template +from footprint.main.models.tag import Tag + +__author__ = 'calthorpe_associates' +logger = logging.getLogger(__name__) + +#@profile +def on_config_entity_post_save_layer(sender, **kwargs): + """ + Sync tilestache to a ConfigEntity class after the latter is saved + :param **kwargs: optional "db_entity_keys" to limit the layers created to those DbEntities + """ + config_entity = kwargs['instance'] + logger.debug("\t\tHandler: on_config_entity_post_save_layer. ConfigEntity: %s" % config_entity.name) + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + + # Only create layers for Scenarios + if not isinstance(config_entity, Scenario): + return + + # Create LayerLibrary instances based on each LayerLibrary configuration if the configuration's scope + # matches that of config_entity + client_layer_configuration = resolve_fixture( + "publishing", + "layer", + LayerConfigurationFixture, + config_entity.schema(), + config_entity=config_entity) + + map( + lambda layer_library_configuration: _update_or_create_layer_library( + config_entity, layer_library_configuration, **kwargs), + client_layer_configuration.matching_scope(client_layer_configuration.layer_libraries(), + class_scope=config_entity.__class__)) + +#@profile +def on_db_entity_post_save_layers(sender, **kwargs): + """ + Update/Create layers for the give DbEntityInterest. + This is just like on_config_entity_post_save_layers except that we only create the layer + for this db_entity + """ + + db_entity_interest = kwargs['instance'] + config_entity = db_entity_interest.config_entity.subclassed_config_entity + db_entity = db_entity_interest.db_entity + logger.debug("\t\tHandler: on_db_entity_post_save_layers. DbEntity: %s" % db_entity.full_name) + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + + # Only create layers for Scenarios + if not isinstance(config_entity, Scenario): + return + + # Create LayerLibrary instances based on each LayerLibrary configuration if the configuration's scope + # matches that of config_entity + client_layer_configuration = resolve_fixture( + "publishing", + "layer", + LayerConfigurationFixture, + config_entity.schema(), + config_entity=config_entity) + + map( + lambda layer_library_configuration: _update_or_create_layer_library( + config_entity, layer_library_configuration, db_entity_keys=[db_entity.key]), + client_layer_configuration.matching_scope(client_layer_configuration.layer_libraries(), + class_scope=config_entity.__class__)) + +def _update_or_create_layer_library(config_entity, layer_library_configuration, **kwargs): + """ + Update or create the LayerLibrary for the given config_entity and configuration. + Also create update or craet all layers of that library unless limited by kwargs['db_entity_keys'] + :param kwargs: 'db_entity_keys': Optional list to limit layer update/create to those DbEntities + """ + db_entity_keys = kwargs.get('db_entity_keys', None) + + logger.debug("\t\t\tUpdate/Create LayerLibrary %s" % layer_library_configuration.key) + layer_library = LayerLibrary.objects.update_or_create( + key=layer_library_configuration.key, + scope=config_entity.schema(), + config_entity=config_entity, + defaults=dict( + configuration=layer_library_configuration, + name=layer_library_configuration.name.format(config_entity.name), + description=layer_library_configuration.description.format(config_entity.name) + ) + )[0] + + db_entity_key_to_configuration = map_to_dict(lambda configuration: [configuration.db_entity_key, configuration], + layer_library_configuration.data.presentation_media_configurations) + if db_entity_keys: + # Configure all given db_entity_keys, whether or not a matching configuration exists. + # This allows us to handle uploaded layers + map(lambda db_entity_key: update_or_create_layer(layer_library, + db_entity_key_to_configuration.get(db_entity_key), + db_entity_key=db_entity_key), + filter(lambda db_entity_key: config_entity.computed_db_entities(key=db_entity_key).count() == 1, + db_entity_keys)) + else: + if not config_entity.origin_config_entity: + # Update or create the Layers of the layer_library_configuration + layers = map( + lambda layer_configuration: update_or_create_layer(layer_library, layer_configuration), + layer_library_configuration.data.presentation_media_configurations) + else: + # If this is a cloned config_entity, clone or update clones + layers = map( + lambda origin_layer: clone_or_update_cloned_layer(layer_library, origin_layer), + Layer.objects.filter(presentation__config_entity=config_entity.origin_config_entity)) + + return layer_library + +def layer_configuration_from_layer(layer, **kwargs): + """ + Reverses the work of update_or_create_layer for cloning. + :param kwargs: overrides + """ + return LayerConfiguration( + **merge( + dict( + db_entity_key=layer.db_entity_key, + layer_library_key=layer.presentation.key, + visible=layer.visible, + visible_attributes=layer.visible_attributes, + built_form_set_key=layer.configuration.get('built_form_key', None), + sort_priority=layer.configuration.get('sort_priority', None), + attribute_sort_priority=layer.configuration.get('attribute_sort_priority', None), + column_alias_lookup=layer.configuration.get('column_alias_lookup', None), + tags=layer.tags.all() + ), + kwargs) + ) + +def clone_or_update_cloned_layer(layer_library, origin_layer): + """ + Clones the given layer into the target_config_entity if it doesn't already exist there + """ + + layer_configuration =layer_configuration_from_layer( + origin_layer, + # Override this in case the layer is being cloned to a library with a different key + layer_library_key=layer_library.key + ) + update_or_create_layer(layer_library, layer_configuration) + +def update_or_create_layer(layer_library, layer_configuration=None, db_entity_key=None): + """ + Create a Layer for each DbEntity in the LayerConfiguration. These instances constitute the default + library of the config_entity, which is a library of all DbEntities. The media of these instances can be set + to style media. + :param layer_configuration: Optional. A full configuration + :param db_entity_key. Optional. Use instead of full configuration to generate defaults + :return: + """ + + if not layer_configuration: + # See if the layer and its configuration exists + layers = Layer.objects.filter(presentation=layer_library, db_entity_key=db_entity_key) + if len(layers) == 1 and layers[0].configuration: + # Restore configuration + logger.debug("\t\t Imported Layer for %s already exists. Updating" % db_entity_key) + layer_configuration = layer_configuration_from_layer(layers[0]) + else: + # Create the configuration + logger.debug("\t\t Importer Layer for %s does not exist. Creating" % db_entity_key) + layer_configuration = create_layer_configuration_for_import(layer_library.subclassed_config_entity, db_entity_key) + + # Resolve the active DbEntity of the ConfigEntity based on the key of the LayerConfiguration + db_entity_key = layer_configuration.db_entity_key + logger.debug("\t\t\tLayer Publishing DbEntity Key: %s" % db_entity_key) + + # In case multiple DbEntities with the same key exist, get the active one + try: + db_entity = layer_library.subclassed_config_entity.computed_db_entities().get(key=db_entity_key) + except Exception, e: + raise Exception( + "db_entity_key {0} does not exist for config_entity {1}. Did you configure the LayerConfiguration for the wrong scope? Original exception: {2}".format( + db_entity_key, layer_library.subclassed_config_entity, e.message)) + + # Get the Template instance for the db_entity_key + # The key is based on the base class of the db_entity_key if one exists + # If no matching template is found use the default Medium + try: + template_id_key = LayerMediumKey.Fab.ricate(layer_library.subclassed_config_entity.abstract_class_of_db_entity(db_entity_key).__name__) + template = Medium.objects.get_subclass( + key=template_id_key + ) + except Exception: + template = Medium.objects.get( + key=LayerMediumKey.DEFAULT + ) + + # Extract the context from the template if there is one. + # This serves as the default medium_context of the Layer, which can later be customized (e.g. a user can specify + # specific colors and other stylistic data) + template_context = template.template_context if isinstance(template, Template) else None + + try: + rendered_medium = template.render_attributed_content(template_context, content_key='css') + except KeyError: + raise Exception( + "For db_entity_key %s could not find one or more of Template content attributes of set: %s in medium_context with attribute(s): %s" % + (db_entity_key, ', '.join(template.content['attributes']), ', '.join(template_context['attributes']))) + + layer = Layer.objects.update_or_create( + presentation=layer_library, + db_entity_key=db_entity_key, + defaults=dict(medium=template, + # The name by default matches the DbEntity + name=db_entity.name, + configuration=dict( + built_form_set_key=layer_configuration.built_form_set_key, + sort_priority=layer_configuration.sort_priority if layer_configuration.sort_priority > 0 else 100, + attribute_sort_priority=layer_configuration.attribute_sort_priority, + column_alias_lookup=layer_configuration.column_alias_lookup, + ), + visible_attributes=layer_configuration.visible_attributes, + + # A copy of the template context. This default context can be updated by the user to customize + # the layer styling + medium_context=template_context, + # A rendered version of the template_context + rendered_medium=rendered_medium, + visible=layer_configuration.visible) + )[0] + + layer.tags.clear() + # Tag the layer with the configured tags or the default + layer.tags.add(*(layer_configuration.tags or + [Tag.objects.update_or_create(tag=layer.presentation.config_entity.db_entity_owner(layer.db_entity_interest.db_entity).key)[0]])) + + # Customize the medium_context to have a class that refers specifically to this layer + # TODO document why this is needed [it is not] + if template_context: + layer.medium_context['htmlClass'] = 'layer_{0}'.format(layer.id) + layer.save() + + # Update or create the LayerSelection tables and instances for this layer + update_or_create_layer_selections_for_layer(layer) + + return layer + +def on_db_entity_save(): + """ + respond to whenever a db entity is added or updated + :return: + """ + + +def on_layer_style_save(): + """ + respond to any changes in style + :return: + """ + + +def on_config_entity_pre_delete_layer(sender, **kwargs): + """ + """ + config_entity = kwargs['instance'] + LayerLibrary.objects.filter( + key=Keys.LAYER_LIBRARY_DEFAULT, + scope=config_entity.key).delete() + + +def on_scenario_feature_save(sender, **kwargs): + """ + this method will call the layer invalidation after a scenario has been edited + :param sender: + :param kwargs: + :return: + """ + scenario = kwargs['instance'] + changed_layers = scenario.layers.filter(db_entity_key__in=1) + + +def update_or_create_layer_selections(layer_libraries=None, config_entity=None): + # For all unique layers (they might be shared by libraries) configure layer_selection tables + layer_libraries_of_config_entity = layer_libraries if layer_libraries else LayerLibrary.objects.filter(config_entity=config_entity) + layers = Layer.objects.filter(presentation__in=layer_libraries_of_config_entity) + # Create a layer_selection class for the layer if it is selectable + for layer in layers: + update_or_create_layer_selections_for_layer(layer) + +def update_or_create_layer_selections_for_layer(layer): + """ + Create LayerSelections for all users for this layer. + """ + layer_selection_class = get_or_create_dynamic_layer_selection_class_and_table(layer) + if layer_selection_class: + logger.debug("\t\t\tCreating LayerSelection for Layer of DbEntity Key: %s" % layer.db_entity_key) + for user in User.objects.all(): + # Create an instance for each user in the system + logger.debug("\t\t\t\t Inserting LayerSelection instance for user: %s" % user.username) + layer_selection_class.objects.update_or_create(user=user) + + +def create_layer_configuration_for_import(config_entity, db_entity_key): + """ + Creates a LayerConfiguration for imported layers using the template + LayerConfigurations designed in the LayerConfigurationFixture.import_layer_configurations + """ + + client_layer_configuration = resolve_fixture( + "publishing", + "layer", + LayerConfigurationFixture, + config_entity.schema(), + config_entity=config_entity) + + layer_configuration = copy.copy(client_layer_configuration.import_layer_configurations()[0]) + # Update the template LayerConfiguration to our db_entity_key + layer_configuration.db_entity_key = db_entity_key + return layer_configuration diff --git a/footprint/main/publishing/origin_db_entity_processor.py b/footprint/main/publishing/origin_db_entity_processor.py new file mode 100644 index 000000000..c6ee961be --- /dev/null +++ b/footprint/main/publishing/origin_db_entity_processor.py @@ -0,0 +1,46 @@ +import logging +from footprint.main.lib.functions import merge, remove_keys +from footprint.main.models import Layer +from footprint.main.models.database.information_schema import InformationSchema, sync_geometry_columns +from footprint.main.models.presentation.layer_selection import get_or_create_dynamic_layer_selection_class_and_table +from footprint.main.publishing.import_processor import ImportProcessor +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.main.publishing.data_import_publishing import create_and_population_associations, add_primary_key_if_needed, DefaultImportProcessor + +logger = logging.getLogger(__name__) + +__author__ = 'calthorpe' + +class OriginDbEntityProcessor(ImportProcessor): + + def importer(self, config_entity, db_entity): + """ + Replaces the normal ImportProcessor importer with one to import a shapefile from disk + """ + user = db_entity.creator + + if InformationSchema.objects.table_exists(db_entity.schema, db_entity.table): + # The table already exists. Skip the import an log a warning + logger.warn("The target table for the layer selection import already exists. Skipping table import.") + else: + feature_class_creator = FeatureClassCreator(config_entity, db_entity) + origin_feature_class_configuration = db_entity.origin_instance.feature_class_configuration + feature_class_configuration = merge( + origin_feature_class_configuration, + # override the origin configuration except for stuff that should mimic it + remove_keys(feature_class_creator.create_feature_class_configuration(), ['fields', 'intersection', 'abstract_class', 'source_from_origin_layer_selection']), + # Erase import configuration properties + dict(import_fields=[], import_ids_only=False)) + + feature_class_creator.merge_feature_class_configuration_into_db_entity(feature_class_configuration) + layer = Layer.objects.get(presentation__config_entity=config_entity, db_entity_key=db_entity.origin_instance.key) + if db_entity.feature_class_configuration.get('source_from_origin_layer_selection'): + layer_selection_class = get_or_create_dynamic_layer_selection_class_and_table(layer, True) + layer_selection = layer_selection_class.objects.get(user=user) + features = layer_selection.selected_features + else: + features = None + + DefaultImportProcessor().peer_importer(config_entity, db_entity, import_from_origin=True, source_queryset=features) + + diff --git a/footprint/main/publishing/policy_publishing.py b/footprint/main/publishing/policy_publishing.py new file mode 100644 index 000000000..a135f8e8a --- /dev/null +++ b/footprint/main/publishing/policy_publishing.py @@ -0,0 +1,114 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +#from memory_profiler import profile + +from footprint.main.models import PolicySet, GlobalConfig +from footprint.main.models.config.policy import Policy +from footprint.main.models.config.scenario import Scenario +from footprint.client.configuration import resolve_fixture +from footprint.client.configuration.fixture import PolicyConfigurationFixture + +__author__ = 'calthorpe_associates' + +def update_or_create_policy_sets(config_entity, **kwargs): + """ + Creates a ResultLibrary and its Result instances upon saving a config_entity if they do not yet exist. + :param config_entity + :param kwargs + :return: + """ + + # Create top-level policy if needed. TODO move to initializer + global_policy = Policy.objects.update_or_create( + key='global', + schema=None, + defaults=dict( + name='Global', + description='The parent policy of all', + values={} + ))[0] + + if isinstance(config_entity, GlobalConfig): + client_policy = resolve_fixture( + "policy", + "policy", + PolicyConfigurationFixture, + config_entity.schema(), + config_entity=config_entity) + + # Create each policy set and store them as a dict keyed by their key + for policy_set_config in client_policy.policy_sets(): + policy_set = PolicySet.objects.update_or_create( + key=policy_set_config['key'], + defaults=dict( + name=policy_set_config['name'], + description=policy_set_config.get('description', None) + ) + )[0] + print 'this is a test change' + policies = map(lambda policy_config: global_policy.update_or_create_policy(policy_config), policy_set_config.get('policies', [])) + policy_set.policies.add(*policies) + config_entity.add_policy_sets(policy_set) + + + elif isinstance(config_entity, Scenario): # and kwargs.get('created', None): + # TODO for now just the first policy_set to the selected one + config_entity.select_policy_set(config_entity.computed_policy_sets()[0]) + config_entity._no_post_save_publishing = True + config_entity.save() + config_entity._no_post_save_publishing = False + + +#@profile +def on_config_entity_post_save_policy(sender, **kwargs): + """ + Sync a ConfigEntity's ResultPage presentation + """ + config_entity = kwargs['instance'] + + update_or_create_policy_sets(config_entity, **kwargs) + +def on_db_entity_save(): + """ + respond to whenever a db entity is added or updated + :return: + """ + pass + +def on_layer_style_save(): + """ + respond to any changes in style ( + :return: + """ + pass + +#@profile +def on_config_entity_post_save(sender, **kwargs): + """ + Sync tilestache to a ConfigEntity class after the latter is saved + """ + config_entity = kwargs['instance'] + + +def on_config_entity_pre_delete_results(sender, **kwargs): + """ + Sync geoserver to a ConfigEntity class after the latter is saved + """ + config_entity = kwargs['instance'] + + diff --git a/footprint/main/publishing/publishing.py b/footprint/main/publishing/publishing.py new file mode 100644 index 000000000..4921b702b --- /dev/null +++ b/footprint/main/publishing/publishing.py @@ -0,0 +1,169 @@ +import logging +import traceback +from django.http import HttpResponse +from django.utils import timezone +from inflection import camelize +import sys +from tastypie.models import ApiKey +from footprint.celery import app +from footprint.common.utils.async import start_and_track_task +from footprint.common.utils.websockets import send_message_to_client +from footprint.main.lib.functions import remove_keys, merge +from footprint.main.models import ConfigEntity +from footprint.main.utils.utils import resolve_module_attr, full_module_path + +logger = logging.getLogger(__name__) + +__author__ = 'calthorpe' + + +def post_save_publishing(signal_path, config_entity, user, **kwargs): + api_key = ApiKey.objects.get(user=user).key + + # Pass the arguments to the task and run via celery. Note that kwargs is being treated + # as a dict here and passed along + instance_id = kwargs['instance'].id + instance_class = full_module_path(kwargs['instance'].__class__) + # Sanity check to make sure this works, since it sometimes fails in the celery task + try: + instance = kwargs.get('instance', resolve_module_attr(instance_class).objects.get(id=instance_id)) + instance_key = kwargs['instance_key'] if kwargs.get('instance_key', None) else instance.key + logger.info("\tPost save instance %s of class %s and id %s" % (instance_key, instance_class, instance_id)) + except Exception, e: + logger.error("What the heck is going on? Unable to resolve instance of class %s and id %s" % (instance_class, instance_id)) + raise e + + + job = start_and_track_task(_post_save_publishing, + api_key, + config_entity, + user, + merge(remove_keys(kwargs, ['instance']), + dict( + signal_path=signal_path, + instance_class=instance_class, + instance_id=instance_id))) + return HttpResponse(job.hashid) + + +@app.task +def _post_save_publishing(job, config_entity, user, kwargs): + """ + Runs all configured publishers via the Django signals they depend upon. + This is done in Celery in order to support long running tasks launched from a client. + Peer tasks are run in parallel. Dependent tasks are called after the tasks they depend on complete + + :param hash_id: Job hash id + :param config_entity: + :param user: The current user or None if no user is in scope + :return: + """ + + try: + instance = kwargs.get('instance', resolve_module_attr(kwargs['instance_class']).objects.get(id=kwargs['instance_id'])) + except Exception, e: + # Mysteriously, sometimes Celery can't load the instance by instance_class and instance_id, even thought the instance + # has already been saved. Try it again and then give up. + try: + import time + time.sleep(0.5) + instance = kwargs.get('instance', resolve_module_attr(kwargs['instance_class']).objects.get(id=kwargs['instance_id'])) + except Exception, e: + raise Exception("Unable to resolve instance of class %s and id %s. Message: %s" % (kwargs['instance_class'], kwargs['instance_id'], e.message)) + + updated_kwargs = merge(dict(instance=instance), remove_keys(kwargs, ['signal_path', 'instance_class', 'instance_id'])) + + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + + # The signal that we'll send to kick off dependent publishers + # We'll alse use it to recurse on dependent signals + signal_path = kwargs['signal_path'] + # Lookup the dependent signal paths of the signal. We'll use these to recurse + dependent_signal_paths = kwargs['dependent_signal_paths'](signal_path) + # We use the prefix of the signal_paths as an event name for the client + # For example 'post_save_config_entity' becomes postSaveConfigEntity here + # We suffix the event name with PublisherCompleted or PublisherFailed + client_event = camelize(kwargs['signal_prefix'], False) + + # Send a message to client announcing which signal completed + # The message sends the camelized version of the signal name + # For instance post_save_config_entity_initial becomes postSaveConfigEntityInitial + # where config_entity_subclass_name is the name of the subclass to make it clear to receivers in javascript what type was receved + signal_proportion_lookup_name = signal_path.split('.')[-1] + publisher_name = unicode( + camelize( + signal_proportion_lookup_name, + False + ) + ) + + instance_key = kwargs['instance_key'] if kwargs.get('instance_key', None) else instance.key + try: + # TODO causing duplicate pk errors in concurrent mode + #job.status = 'Started' + #job.save() + + logger.debug("\tRunning handlers for signal {signal_path} for key {key}".format( + config_entity=config_entity, + username=user.username, + signal_path=signal_path, + key=instance_key)) + + # Send the signal. The listening publishers will run in sequence + resolve_module_attr(signal_path).send(sender=config_entity.__class__, **updated_kwargs) + event = '%sPublisherCompleted' % client_event + logger.debug("Sending message %s for signal complete %s to client with key %s" % (event, signal_proportion_lookup_name, instance_key)) + + send_message_to_client(user.id, dict( + event=event, + job_id=str(job.hashid), + config_entity_id=config_entity.id, + config_entity_class_name=config_entity.__class__.__name__, + # Send the key since the id of new instances might be meaningless to the client + # If it hasn't updated the record's id yet + key=instance_key, + publisher_name=publisher_name, + class_name=instance.__class__.__name__, + id=instance.id, + # Send the proportion of work that completing this signal signifies--0 to 1 + proportion=kwargs.get('signal_proportion_lookup', {}).get(signal_proportion_lookup_name, 0) + )) + + # Find all dependent signals of this one and run each in parallel + for dependent_signal_path in dependent_signal_paths: + post_save_publishing( + dependent_signal_path, + config_entity, + user, + **updated_kwargs + ) + job.status = 'Complete' + + except Exception, e: + if kwargs.get('created'): + # Let the instance delete itself on a post save creation error + instance.handle_post_save_create_error() + + job.status = "Failed" + exc_type, exc_value, exc_traceback = sys.exc_info() + readable_exception = traceback.format_exception(exc_type, exc_value, exc_traceback) + job.data = readable_exception + event = '%sPublisherFailed' % client_event + logger.debug("Sending Failed message %s for signal %s to client with key %s" % (event, signal_proportion_lookup_name, instance_key)) + send_message_to_client(user.id, dict( + event=event, + config_entity_id=config_entity.id, + config_entity_class_name=config_entity.__class__.__name__, + # Send the key since the id of new instances might be meaningless to the client + # If it hasn't updated the record's id yet + key=instance_key, + class_name=instance.__class__.__name__, + id=instance.id, + publisher_name=publisher_name, + trace=readable_exception) + ) + raise Exception(readable_exception) + finally: + job.ended_on = timezone.now() + job.save() diff --git a/footprint/main/publishing/result_initialization.py b/footprint/main/publishing/result_initialization.py new file mode 100644 index 000000000..21b69c017 --- /dev/null +++ b/footprint/main/publishing/result_initialization.py @@ -0,0 +1,223 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.dispatch import receiver +from footprint.client.configuration.fixture import ResultConfigurationFixture +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.models.geospatial.db_entity_configuration import db_entity_configuration_keys +from footprint.main.models.signals import initialize_media +from footprint.main.lib.functions import dual_map_to_dict, map_to_dict, get_first, filter_keys, merge +from footprint.main.models.presentation.medium import Medium +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.utils import expect, resolvable_model_name +from footprint import settings + +__author__ = 'calthorpe_associates' + +@receiver(initialize_media) +def initialize_result_media(sender, **kwargs): + """ + This fires when the application initializes or updates. It creates style templates and their default contexts + for the results + :param sender: + :param kwargs: + :return: + """ + client_result = resolve_fixture( + "publishing", + "result", + ResultConfigurationFixture, + settings.CLIENT) + client_result.update_or_create_media() + +class ResultLibraryKey(Keys): + class Fab(Keys.Fab): + @classmethod + def prefix(cls): + return 'result_library' + + # The default result library + DEFAULT = Fab.ricate('default') + +class ResultKey(Keys): + class Fab(Keys.Fab): + @classmethod + def prefix(cls): + return 'result' + BASE_EMPLOYMENT_BY_TYPE = Fab.ricate('base_employment_by_type') + BASE_DWELLING_UNITS_BY_TYPE = Fab.ricate('base_dwelling_units_by_type') + INCREMENTS = Fab.ricate('increments') + INCREMENTS_EMPLOYMENT_BY_TYPE = Fab.ricate('increments_employment_by_type') + INCREMENTS_DWELLING_UNITS_BY_TYPE = Fab.ricate('increments_dwelling_units_by_type') + INCREMENTS_BARS = Fab.ricate('increments_bars') + END_STATE = Fab.ricate('end_state') + END_STATE_EMPLOYMENT_BY_TYPE = Fab.ricate('end_state_employment_by_type') + END_STATE_DWELLING_UNITS_BY_TYPE = Fab.ricate('end_state_dwelling_units_by_type') + END_STATE_BARS = Fab.ricate('end_state_bars') + FISCAL = Fab.ricate('fiscal') + VMT = Fab.ricate('vmt') + +class ResultMediumKey(ResultKey): + class Fab(Keys.Fab): + @classmethod + def prefix(cls): + return 'result__medium' + + # The default medium for all results + DEFAULT = Fab.ricate('default') + BASE_EMPLOYMENT_BY_TYPE = Fab.ricate('base_employment_by_type') + BASE_DWELLING_UNITS_BY_TYPE = Fab.ricate('base_dwelling_units_by_type') + INCREMENTS = Fab.ricate('increments') + INCREMENTS_DWELLING_UNITS_BY_TYPE = Fab.ricate('increments_dwelling_units_by_type') + INCREMENTS_EMPLOYMENT_BY_TYPE = Fab.ricate('increments_employment_by_type') + END_STATE = Fab.ricate('end_state') + END_STATE_EMPLOYMENT_BY_TYPE = Fab.ricate('end_state_employment_by_type') + END_STATE_DWELLING_UNITS_BY_TYPE = Fab.ricate('end_state_dwelling_units_by_type') + END_STATE_BARS = Fab.ricate('end_state_bars') + + +class ResultLibraryConfiguration(object): + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + expect(self, 'key', 'name', 'description') + # The optional ConfigEntity class scope of the Result. Only config_entities that match or inherit this will create a result_library + class_scope=None, + key=None, + name=None, + description=None + +class ResultConfiguration(object): + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + # The optional ConfigEntity class scope of the Result. Only config_entities that match or inherit this will create a result + class_scope = None + # The key of the ResultLibrary to which this Result belongs + # This could be refactored to allow multiple libraries + result_library_key = None + # The result type 'bar_graph', 'analytic_bars' + result_type = None + # The key of the created DbEntity for the Result. These must be unique across a config_entity + result_db_entity_key = None, + name = None, + # The class attributes to show in the result. These are used for column names, data, control totals, etc. + attributes = [], + # Localized and presentable name for attributes, by matching array order + labels = [], + # A dict mapping each attribute to the database column name returned by the query, without the __sum, __avg, etc suffix + db_column_lookup = {}, + # Maps each attribute to a dict with a min and max to use for extents for result presentations that need them static + extent_lookup = {}, + # Is the bar graph stackable + stackable = False, + # Is the bar graph stacked initially + is_stacked = False, + # Indicates that control total lines should be drawn on the graph + include_control_totals = True + # The source DbEntity from which the Result's DbEntity is cloned + source_db_entity_key = None + # Query lambda for the Result + create_query = None + + def get_presentation_medium_configuration(self): + """ + Extracts the essential information needed by the Result configuration attribute + :return: + """ + return dict( + # Create a dict that translates the column names to labels + column_to_label=self.create_column_to_label(), + # Create a dict that translates the column names to attribute names (used by the UI) + attribute_to_column=self.db_column_lookup, + # fixes attribute order + attributes=self.attributes, + extent_lookup=self.extent_lookup, + stackable=self.stackable, + is_stacked=self.is_stacked, + result_type=self.result_type, + # Set a control total to 0 for each column + control_totals=self.create_control_totals() if self.include_control_totals else [], + sort_priority=self.sort_priority + ) + + def update_or_create_db_entity(self, config_entity): + """ + Clone the DbEntity from the increments DbEntity + This means the queryset is run on the Increments class manager + + :param config_entity: + :return: Return the DbEntity + """ + + return self.clone_db_entity_and_interest_for_result( + config_entity, + self.source_db_entity_key, + key=self.result_db_entity_key, + name=self.name, + query=self.create_query(self) + ).db_entity + + def clone_db_entity_and_interest_for_result(self, config_entity, reference_db_entity_key, **kwargs): + """ + Clone the selected db_entity of key reference_db_entity and replace any of its attributes with those + specified in **kwargs. Normally **kwargs will contain a unique key, unless another version of the table + with the same key is desired, such a version that filters values by query, but that is probably a bad + practice. **kwargs must contain at least one a name to distinguish it from the source DbEntity + :param reference_db_entity_key: key of the DbEntity to clon + :param kwargs: replacement values containing at the very least 'key' + :return: The DbEntityInterest which references the cloned db_entity + """ + db_entity = config_entity.computed_db_entities().get(key=reference_db_entity_key) + # Avoid circular reference + from footprint.main.publishing.db_entity_publishing import clone_or_update_db_entity_and_interest + return clone_or_update_db_entity_and_interest(config_entity, db_entity, **merge(kwargs, dict(override_on_update=True))) + + def create_column_to_label(self): + """ + Create a mapping between table column names and labels, based on the attribute names + :return: + """ + return dual_map_to_dict( + lambda attribute, label: [self.db_column_lookup[attribute], label], + self.attributes, + self.labels) + + def create_control_totals(self): + """ + Create a dict of initial control totals + :return: + """ + return map_to_dict( + lambda column: [column, 0], + self.attributes) + + def resolve_result_medium(self): + """ + Get the Medium of the result key or default + :return: + """ + return get_first( + # See if a Medium exists with a key corresponding to the result + # Default to the default Medium for results + Medium.objects.filter(key=self.result_db_entity_key.replace('result', 'result__medium')), + Medium.objects.get(key=ResultMediumKey.DEFAULT) + ) +class ResultSort(object): + FUTURE = 10 + BASE = 20 + OTHER = 60 + BACKGROUND = 80 diff --git a/footprint/main/publishing/result_publishing.py b/footprint/main/publishing/result_publishing.py new file mode 100644 index 000000000..ed9d2a172 --- /dev/null +++ b/footprint/main/publishing/result_publishing.py @@ -0,0 +1,138 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +# from memory_profiler import profile +import logging +from footprint.client.configuration.fixture import ResultConfigurationFixture +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.lib.functions import map_to_dict +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.models.config.db_entity_interest import DbEntityInterest + +from footprint.main.models.config.scenario import Scenario +from footprint.main.models.presentation.result_library import ResultLibrary +from footprint.main.models.presentation.result import Result + +logger = logging.getLogger(__name__) + +__author__ = 'calthorpe_associates' + +#@profile +def on_config_entity_post_save_result(sender, **kwargs): + """ + Sync a ConfigEntity's ResultPage presentation + :param kwargs: 'db_entity_keys' Optional list to limit which DbEntities are processed + """ + config_entity = kwargs['instance'] + logger.debug("\t\tHandler: on_config_entity_post_save_result. ConfigEntity: %s" % config_entity.name) + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + update_or_create_result_libraries(config_entity) + +#@profile +def on_db_entity_post_save_result(sender, **kwargs): + db_entity_interest = kwargs['instance'] + config_entity = db_entity_interest.config_entity + db_entity = db_entity_interest.db_entity + logger.debug("\t\tHandler: on_db_entity_post_save_layer. DbEntity: %s" % db_entity.full_name) + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + update_or_create_result_libraries(config_entity, db_entity_keys=[db_entity.key]) + +def update_or_create_result_libraries(config_entity, **kwargs): + """ + Creates a ResultLibrary and its Result instances upon saving a config_entity if they do not yet exist. + :param config_entity + :param kwargs: 'db_entity_keys' Optional list to limit the Results processed. Any result whose + result_db_entity_key or source_db_entity_key is in db_entity_keys will pass through. + :return: + """ + + # Just process Scenarios. Projects, etc will probably have Results in the future + if not isinstance(config_entity, Scenario): + return + + db_entity_keys = kwargs.get('db_entity_keys', None) + + client_result = resolve_fixture( + "publishing", + "result", + ResultConfigurationFixture, + config_entity.schema(), + config_entity=config_entity) + + # Create each ResultLibrary and store them as a dict keyed by their key + result_library_lookup = map_to_dict(lambda result_library_config: [ + result_library_config.key, + ResultLibrary.objects.update_or_create( + key=result_library_config.key, + config_entity=config_entity, + scope=config_entity.schema(), + defaults=dict( + name=result_library_config.name.format(config_entity.name), + description=result_library_config.description.format(config_entity.name) + ) + )[0]], + client_result.result_libraries()) + + #for key, result_library in result_library_lookup.items(): + # result_library.results.all().delete() + + # Create each configured Result + for result_config in filter(lambda result: + not db_entity_keys or + result.result_db_entity_key in db_entity_keys or + result.source_db_entity_key in db_entity_keys, + client_result.results()): + + logger.debug("\t\t\tResult Publishing Result DbEntity Key: %s" % result_config.result_db_entity_key) + # Create the db_entity and db_entity_interest for the result + db_entity = result_config.update_or_create_db_entity(config_entity) + # Make the db_entity the default selected one for its key + config_entity.select_db_entity_of_key(db_entity.key, db_entity) + config_entity._no_post_save_publishing = True + config_entity.save() + config_entity._no_post_save_publishing = False + + + # Test the query + db_entity.parse_query(config_entity) + + # Create a result for each result key given. + Result.objects.update_or_create( + # Match the Result to the right ResultLibrary + presentation=result_library_lookup[result_config.result_library_key], + db_entity_key=result_config.result_db_entity_key, + defaults=dict( + # Use the Result's custom Medium, keyed by the Result key + medium=result_config.resolve_result_medium(), + configuration=result_config.get_presentation_medium_configuration()) + ) + # Remove orphan results and their DbEntityInterests/DbEntities + result_library_ids = map(lambda result_library: result_library.id, ResultLibrary.objects.filter(config_entity=config_entity)) + valid_result_keys = map(lambda result_config: result_config.result_db_entity_key, client_result.results()) + orphan_results = Result.objects.filter(presentation__id__in=result_library_ids).exclude(db_entity_key__in=valid_result_keys) + DbEntityInterest.objects.filter(config_entity=config_entity, db_entity__key__in=map(lambda result: result.db_entity_key, orphan_results)).delete() + orphan_results.delete() + + +def on_config_entity_pre_delete_result(sender, **kwargs): + """ + """ + config_entity = kwargs['instance'] + + diff --git a/footprint/main/publishing/shapefile_processor.py b/footprint/main/publishing/shapefile_processor.py new file mode 100644 index 000000000..e534db6e4 --- /dev/null +++ b/footprint/main/publishing/shapefile_processor.py @@ -0,0 +1,99 @@ +import logging +import stat +from footprint.main.database.import_data import ImportData +from footprint.main.models.database.information_schema import InformationSchema, sync_geometry_columns +from footprint.main.publishing.import_processor import ImportProcessor +import zipfile +import os +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.main.publishing.data_import_publishing import create_and_population_associations, add_primary_key_if_needed +from django.conf import settings +from footprint.main.utils.utils import timestamp + +logger = logging.getLogger(__name__) + +__author__ = 'calthorpe' + +class ShapefileProcessor(ImportProcessor): + + def __init__(self, **kwargs): + self.srid = kwargs['db_entity'].srid # Required + super(ShapefileProcessor, self).__init__() + + def importer(self, config_entity, db_entity): + """ + Replaces the normal ImportProcessor importer with one to import a shapefile from disk + """ + user = db_entity.creator + + if InformationSchema.objects.table_exists(db_entity.schema, db_entity.table): + # The table already exists. Skip the import an log a warning + logger.warn("The target table for the shapefile import already exists. Skipping table import.") + else: + zipfile = db_entity.url.replace('file://', '') + shp, files_to_import = unpack_shapefile(zipfile, db_entity.key, user) + import_shapefile_to_db(config_entity, db_entity, shp) + + # Add our normal primary key in the id column if needed + add_primary_key_if_needed(db_entity) + + feature_class_creator = FeatureClassCreator(config_entity, db_entity) + # Inspect the imported table to create the feature_class_configuration + feature_class_configuration = feature_class_creator.feature_class_configuration_from_introspection() + + # Merge the created feature_class_configuration with the on already defined for the db_entity + feature_class_creator.merge_feature_class_configuration_into_db_entity(feature_class_configuration) + logger.debug("Finished shapefile import for DbEntity: %s, feature_class_configuration: %s" % (db_entity, db_entity.feature_class_configuration)) + + # Create association classes and tables and populate them with data + create_and_population_associations(config_entity, db_entity) + +def unpack_shapefile(zip_archive, name, user): + archive = zipfile.ZipFile(zip_archive, 'r') + archive_contents = archive.namelist() + + # verify that each file in the archive corresponds with a known shapefile type and is not + # named with any strings that may compromise our servers + for a in archive_contents: + valid = False + for shp_type in settings.SHAPEFILE_TYPES: + if a.endswith(shp_type) and not a.startswith('/') and not ('..') in a: + valid = True + if not valid: + raise Exception("Inspection of shapefile failed. Remove any extra files and make sure that filenames" + "do not use special characters") + + upload_media_path = os.path.join(settings.MEDIA_ROOT, 'uploads') + # if the files seem okay, extract them to the media root + archive.extractall(path=upload_media_path) + + files_to_import = [] + nice_name = "{username}_{geom_name}_{timestamp}".format(username=user.username, + geom_name=name, + timestamp=timestamp()) + + for a in archive_contents: + imported_name = os.path.join(upload_media_path, a) + + new_name = os.path.join(upload_media_path, nice_name + a[-4:]) + + os.rename(imported_name, new_name) + + b = a.replace(a[:-4], name) + if a.endswith('.shp'): + shp = new_name + + files_to_import.append(b) + + return shp, files_to_import + + +def import_shapefile_to_db(config_entity, db_entity, shp): + upload_media_path = os.path.join(settings.MEDIA_ROOT, 'uploads') + shapefile_path = os.path.join(upload_media_path, shp), + # Update the db_entity.url from the zip file url to the shapefile_path + # This lets ImportData find it. + db_entity.url = 'file://%s' % shapefile_path + db_entity.save() + + ImportData(config_entity=config_entity, db_entity_key=db_entity.key).run() diff --git a/footprint/main/publishing/tilestache_publishing.py b/footprint/main/publishing/tilestache_publishing.py new file mode 100644 index 000000000..825712e48 --- /dev/null +++ b/footprint/main/publishing/tilestache_publishing.py @@ -0,0 +1,471 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +# from memory_profiler import profile +import os +import logging +import shutil +import TileStache +from TileStache.Config import buildConfiguration +from footprint.main.models.presentation.layer import Layer +from footprint.main.models.presentation.layer_library import LayerLibrary +from footprint.main.models.presentation.tilestache_config import TileStacheConfig +from footprint.main.models.config.scenario import Scenario +from footprint.main.models.config.config_entity import ConfigEntity + +import shlex +import subprocess +import json +from django.template import Template, Context +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.utils import database_settings, create_static_content_subdir, get_property_path +from TileStache.Config import _parseConfigfileLayer +from django.contrib.auth.models import User +from footprint.main.lib.functions import merge, unique +from footprint.main.models.presentation.layer_selection import get_or_create_dynamic_layer_selection_class_and_table +from footprint.main.utils.utils import connection_dict +from django.conf import settings +logger = logging.getLogger(__name__) + +#@profile +def on_config_entity_post_save_tilestache(sender, **kwargs): + """ + Update/Create the tilestache data for the layers given DbEntityInterest + :param: kwargs: Optional db_entity_keys to limit the layers to those of the given keys + """ + config_entity = kwargs['instance'] + logger.debug("\t\tHandler: on_config_entity_post_tilestache. ConfigEntity: %s" % config_entity.name) + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + if not isinstance(config_entity, Scenario): + return + + _on_post_save_tilestache(config_entity, **kwargs) + +#@profile +def on_db_entity_post_save_tilestache(sender, **kwargs): + """ + Update/Create the tilestache data for the layer(s) of the given DbEntityInterest + """ + + logger.debug("\t\tHandler: on_db_entity_post_save_tilestache") + if ConfigEntity._heapy: + ConfigEntity.dump_heapy() + + db_entity_interest = kwargs['instance'] + config_entity = db_entity_interest.config_entity.subclassed_config_entity + db_entity = db_entity_interest.db_entity + _on_post_save_tilestache(config_entity, db_entity_keys=[db_entity.key]) + +def _on_post_save_tilestache(config_entity, **kwargs): + """ + Update/Create of the tilestache data for layers of the config_entity + :param: config_entity + :param: kwargs: Optional db_entity_keys to limit the layers to those of the given keys + """ + + layer_libraries = LayerLibrary.objects.filter( + scope=config_entity.schema(), + config_entity=config_entity + ) + layers = Layer.objects.filter(presentation__in=layer_libraries, + db_entity_key__in=kwargs['db_entity_keys']) if kwargs.get('db_entity_keys') else \ + Layer.objects.filter(presentation__in=layer_libraries) + tilestache_config, created, updated = TileStacheConfig.objects.update_or_create(name='default') + modify_config(config_entity, tilestache_config, layers) + +def modify_config(config_entity, tilestache_config, layers): + """ + Using the attributes of the given Layer objects, updates the default tilestache configuration object and stores + it in the database. Each layer instance is used to create a raster, vector, and selection (vector) layer for + each configured attribute. These layers are keyed uniquely by the layer instance id, attribute, and layer type, + so they will simply overwrite themselves in the config and accumulate. + + :param tilestache_config: + :param layers: + :return: + """ + + if tilestache_config.enable_caching: + cache = { + "name": "Disk", + "path": "/tmp/stache", + "umask": "0000" + } + + else: + cache = {"name": "Test"} + + # Get or define the config dict + config = tilestache_config.config if isinstance(tilestache_config.config, TileStache.Config.Configuration) else buildConfiguration({ + 'logging': "info", + 'layers': {}, + 'cache': cache, + }) + + for layer in layers: + # If the layer contains a style configuration + if layer.medium_context: + + # Render the raster styles for each attribute styled in the layer's medium + render_attribute_styles(layer) + + # Create a vector, raster, and vector select layer for each attribute listed in the medium_context + for attribute, medium_context in layer.medium_context['attributes'].items(): + logger.debug("\t\t\t: Creating tilestache layer for layer %s, attribute %s" % (layer.db_entity_key, attribute)) + create_vector_layer(config_entity, layer, attribute, config) + create_raster_layer(config_entity, layer, attribute, config) + create_layer_selection(config_entity, layer, attribute, config) + invalidate_cache(layer, config) + + tilestache_config.config = config + tilestache_config.save() + + +def on_config_entity_pre_delete_tilestache(sender, **kwargs): + pass + + +def create_vector_layer(config_entity, layer, attribute, config): + # If the db_entity doesn't have an explicit query create a query from the table and schema that joins + # in the geography column. + db_entity = layer.db_entity_interest.db_entity + query = create_query(attribute, config_entity, layer) + connection = connection_dict(layer.presentation.config_entity.db) + vector_layer = { + "metatile": { + "rows": 6, + "columns": 6, + "buffer": 50, + }, + 'provider': { + 'name': 'vector', + 'driver': 'postgis', + 'clipped': False, + 'parameters': merge( + connection, + dict( + query=query, + column="wkb_geometry", + ) + ), + # 'properties': fields, + + }, + 'allowed origin': "*", + 'id_property': db_entity._meta.pk.name + } + config.layers["layer_{0}_{1}_vector".format(layer.id, attribute)] = \ + _parseConfigfileLayer(vector_layer, config, '') + + +def create_raster_layer(config_entity, layer, attribute, config): + raster_layer = { + "metatile": { + "rows": 6, + "columns": 6, + "buffer": 50, + }, + 'provider': { + 'name': 'mapnik', + 'mapfile': layer.rendered_medium[attribute]['cartocss'] + } + } + config.layers["layer_{0}_{1}_raster".format(layer.id, attribute)] = _parseConfigfileLayer(raster_layer, config, '') + + +def create_query(attribute, config_entity, layer): + db_entity = layer.db_entity_interest.db_entity + feature_class = config_entity.feature_class_of_db_entity_key(db_entity.key) + # Create a query that selects the wkb_geometry and the attribute we need + # There's nothing to prevent styling multiple attributes in the future + try: + query = str(feature_class.objects.values(*unique(['wkb_geometry', attribute])).query) + except Exception, e: + raise Exception("Error creating the query for db_entity %s. Original exception: %s" % (db_entity.key, e.message)) + column_alias = get_property_path(layer.configuration, 'column_alias_lookup.{0}'.format(attribute)) + # This would be better done by values() supporting aliases: + # https://code.djangoproject.com/attachment/ticket/16735/column_alias.diff + # There is a patch available at https://code.djangoproject.com/attachment/ticket/16735/column_alias.diff that could be applied instead of this: + # Replace the select column with the colum as alias. Only 1 replacement is done to avoid mutilating a join with the same column + updated_query = query.replace( + '{column_alias}"'.format(column_alias=column_alias), '{column_alias}" as {attribute}'.format( + column_alias=column_alias, attribute=attribute), 1) if column_alias else query + return updated_query + + +def create_layer_selection(config_entity, layer, attribute, config): + db_entity = layer.db_entity_interest.db_entity + connection = connection_dict(layer.presentation.config_entity.db) + + for user in User.objects.all(): + # Each layer has a dynamic class representing its SelectedFeature table + get_or_create_dynamic_layer_selection_class_and_table(layer) + layer_selection_class = get_or_create_dynamic_layer_selection_class_and_table(layer) + # Each LayerSelection instance is per user + layer_selection = layer_selection_class.objects.get(user=user) + layer_selection._meta._related_many_to_many_cache = None + layer_selection._meta._related_objects_cache = None + # Extract the query from the QuerySet + query = str(layer_selection.selected_features.values('wkb_geometry').query) + + vector_selection_layer = { + 'provider': { + 'name': 'vector', + 'driver': 'postgis', + 'clipped': False, + + 'parameters': merge( + connection, + dict( + query=query, + column="wkb_geometry", + ) + ), + # 'properties': fields, + + }, + 'write cache': False, + 'allowed origin': "*", + 'id_property': db_entity._meta.pk.name + } + + vector_selection_layer = _parseConfigfileLayer(vector_selection_layer, config, '') + # TODO we'll need to use raster at higher zoom levels + # raster_selection_layer = _parseConfigfileLayer(raster_layer, config, '') + + config.layers["layer_{0}_{1}_{2}_selection".format(layer.id, attribute, user.id)] = vector_selection_layer + # config.layers["layer_{0}_{1}_{2}_raster".format(db_entity_id, attribute)] = raster_layer + + +def style_id(layer): + layer_library = layer.presentation + return 'style__{0}__{1}__{2}'.format(layer_library.subclassed_config_entity.id, layer_library.id, layer.db_entity_key) + +def render_attribute_styles(layer): + """ + Iterates through the Layer.medium_context attributes, using the context stored for each attribute to render + the css and cartocss style templates. The results are placed in the layer.rendered_medium in the form + dict(attribute1:dict('css':rendered_css, 'cartocss': rendered_carto_css)). The carto_css is actually a file + path since this is required by tilestache (TODO that it can't be a string) + :param layer: + :return: + """ + + # Fetch the dictionary of styles data for each styled attribute + style = layer.medium_context + style['id'] = style_id(layer) + + # Write the style to the filesystem + for attribute, attribute_context in layer.medium_context['attributes'].items(): + layer.rendered_medium[attribute] = {} + layer.rendered_medium[attribute]['css'] = make_css(layer, attribute) + layer.rendered_medium[attribute]['cartocss'] = make_carto_css(layer, attribute) + + layer.save() + + return layer + + +def make_css(layer, attribute): + """ + Loads the SVG CSS for the given attribute name + :param layer: The Layer instance + :param attribute: An attribute/column of the DbEntity, such as built_form_id + :return: + """ + + # Take the possibly customized context of the layer. If the dict has not been customized, it will simply + # match the default default layer.medium.template_context.context + customized_context = Context(layer.medium_context) + formatted_style = Template(layer.medium.content['attributes'][attribute]['css']).render(customized_context) + return formatted_style + + +def make_carto_css(layer, attribute): + """ + Renders a mapnik XML file based on the properties of the layer and, optionally, + style attributes. This process first writes an MML file to the filesystem. It then invokes the node.js + carto command to create a carto xml file + + :param layer: + :param attribute: + :return: + """ + mml = make_mml(layer, attribute) + xml_filepath = carto_css(mml, style_id(layer)) + return xml_filepath + + +def make_mml(layer, attribute): + """ + Generates mml string from a layer and a style + :param layer: Layer object + :param attribute: the attribute of the layer object that is getting styled + + :return: + """ + carto_css_style = make_carto_css_style(layer, attribute) + #sys.stdout.write(str(carto_css_style)) + db = database_settings(layer.presentation.config_entity.db) + query = create_query(attribute, layer.presentation.config_entity, layer) + db_entity = layer.db_entity_interest.db_entity + # Get the base version of the feature class that holds wkb_geometry + feature_class = layer.presentation.config_entity.db_entity_feature_class(db_entity.key, base_class=True) + + mml = { + "Layer": [ + { + "Datasource": { + "dbname": db['NAME'], + "extent": "", + "geometry_field": "wkb_geometry", + "host": "localhost", + "password": db['PASSWORD'], + "port": db['PORT'], + "srid": 4326, + # Put the query in the table property as a subquery + # Mapnik will wrap this in SELECT AsBinary('wkb_geometry') as subquery WHERE 'wkb_geometray' and ...bounds... + "table": '(%s) as foo' % query, + # The base feature_class holds wkb_geometry + "geometry_table": feature_class._meta.db_table, + "type": "postgis", + "user": db['USER'], + }, + "id": layer.id, + "name": style_id(layer), + + #TODO: look up layer tag from the library so that we can use this function for any layer + "class": db_entity.key, + + #TODO: look up geometry type from the geometry_columns table + "geometry": "polygon", + "srs": Keys.SRS_4326, + }, + ], + "Stylesheet": [carto_css_style], + "interactivity": True, + "maxzoom": 15, + "minzoom": 7, + "format": "png", + "srs": Keys.SRS_GOOGLE, + + } + #sys.stdout.write(str(mml)) + return json.dumps(mml) + + +def make_carto_css_style(layer, attribute): + """ + :param layer: the layer to be styled + :param attribute: The attribute whose cartocss template is to be rendered with the layer.medium_context as the + template context + :return dict with an id in the form {layer.name}.mss and data key valued by the rendered template + """ + + style_template = Template(layer.medium.content['attributes'][attribute]['cartocss']) + context = Context(layer.medium_context) + formatted_style = style_template.render(context) + return { + 'id': '{0}.mss'.format(style_id(layer)), + 'data': "{0}".format(formatted_style) + } + +def carto_css(mml, name): + """ + Takes MML string input and writes it to a Mapnik XML file. + :param mml: an mml string, containing the proper CartoCSS styling and connection attributes required by the + CartoCSS conversion program + :param name: the unique name of the layer (standard method is to name it with its database schema and table name) + :return mapfile: a cascadenik-ready document. + """ + + create_static_content_subdir('cartocss') + mmlFile = "{0}/cartocss/{1}.mml".format(settings.MEDIA_ROOT, name) + mapFile = mmlFile.replace(".mml", ".xml") + f = open(mmlFile, 'w+') + f.write(mml) + f.close() + + logger.debug("Path: %s" % os.environ['PATH']) + carto_css_command = shlex.split("carto {0} > {1}".format(mmlFile, mapFile)) + logger.debug("Running carto: %s" % carto_css_command) + carto_css_content = None + try: + carto_css_content = subprocess.check_output(carto_css_command) + f = open(mapFile, 'w') + f.write(carto_css_content) + f.close() + + except Exception, e: + logger.error("Failed to generate cartocss for {mml}. Exception: {message}. Fix the mml and run footprint_init --tilestache --skip".format(mml=mmlFile, message=e.message, carto_output=carto_css_content)) + raise e + + return mapFile + +def on_post_analytic_run_tilestache(sender, **kwargs): + """ + Responds to analytic module runs + kwargs: module is the string name of the module (TODO covert to instance) + config_entity is the config_entity that ran + :return: + """ + + module = kwargs['module'] + config_entity = kwargs['config_entity'] + + logger.debug("\t\tHandler: on_post_analytic_run_tilestache. ConfigEntity: %s. Module %s" % (config_entity.name, module)) + + if module == 'core': + CORE_DEPENDENT_DB_ENTITY_KEYS = [Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE, Keys.DB_ABSTRACT_END_STATE_FEATURE, + Keys.DB_ABSTRACT_INCREMENT_FEATURE] + layer_libraries = LayerLibrary.objects.filter(config_entity=config_entity) + layers = Layer.objects.filter(presentation__in=layer_libraries, db_entity_key__in=CORE_DEPENDENT_DB_ENTITY_KEYS) + # Invalidate these layers + for layer in layers: + invalidate_cache(layer, TileStacheConfig.objects.get().config) + +def on_layer_selection_post_save_layer(sender, layer=None, user=None): + """ + Invalidate the layer selection cache + :param sender: + :param layer + :param user + :return: + """ + invalidate_cache(layer, TileStacheConfig.objects.get().config, user, ['selection']) + +def invalidate_cache(layer, config, user=None, layer_types=None): + """ + Invalidates the entire cache folder for the layer + :return: + """ + tilestache_cache_path = getattr(config.cache, 'cachepath', None) + if not tilestache_cache_path: + return + + visible_attr = layer.visible_attributes[0] or None + + tilestache_layer_name = 'layer_{id}{visible_attribute}'.format( + id=layer.id, + visible_attribute='_{0}'.format(visible_attr) if visible_attr else '' + ) + + for layer_type in layer_types or ['raster', 'vector', 'selection']: + if layer_type == 'selection' and not user: + continue + layer_cache = shlex.os.path.join(tilestache_cache_path, tilestache_layer_name + "{user_id}_{layer_type}".format( + user_id='_{0}'.format(user.id) if user else '', + layer_type=layer_type + )) + if shlex.os.path.exists(layer_cache): + shutil.rmtree('{cache}'.format(cache=layer_cache)) + diff --git a/footprint/main/publishing/tilestache_style_configuration.py b/footprint/main/publishing/tilestache_style_configuration.py new file mode 100644 index 000000000..9bdadd1cf --- /dev/null +++ b/footprint/main/publishing/tilestache_style_configuration.py @@ -0,0 +1,183 @@ +import os +from inspect import getmro + +from footprint.main.publishing.layer_initialization import LayerMediumKey +from footprint.main.lib.functions import map_to_dict +from footprint.main.models.presentation.template import Template +from footprint.main.models.keys.keys import Keys +from django.conf import settings + +__author__ = 'calthorpe_associates' + + +def create_template_context_dict_for_parent_model(parent_model, color_resolver, related_field=None): + """ + Creates the context_dict for the given model by iterating through all of its child instances and styling + its parent foreign_key (e.g. BuiltForm subclasses' parent foreign key is built_form_id) + :param parent_model: The model that has a parent foreign key + :param color_resolver: A function that accepts the instance as an argument and returns the correct color. + :param related_field: Optional string to specify the related field name to reach the model whose parent + is specified here. For instance if FooFeatures has ManyToMany built_forms, which is a collection of BuiltForm subclass instance, + then related_field should be 'built_forms'. If built_form is simply a ForeignKey on the source model, then + this field isn't needed + :return: A complete default context_dict for all instances of the give model + """ + # Using the parent foreign key (e.g. builtform_id instead of id, seems unneeded) + #parent_foreign_key = '%s_%s' % (parent_model._meta.module_name, parent_model._meta.pk.attname) + parent_foreign_key = '%s' % parent_model._meta.pk.attname + attribute = '{0}__{1}'.format(related_field, parent_foreign_key) if related_field else parent_foreign_key + context_dict = null_context_dict([attribute]) + try: + all = list(parent_model.objects.all()) + except Exception: + from django.db import connection + connection._rollback() + all = list(parent_model.objects.all()) + + for instance in all: + color = color_resolver(instance) + if color: + try: + context_dict['attributes'][attribute]['equals'][instance.id] = color_resolver(instance) + except KeyError, e: + raise Exception("KeyError for context_dict['attributes']['%s']['equals'][%s]. Original Exception: %s" % (parent_foreign_key, instance.id, e.message)) + + return context_dict + + +def create_template_context_dict_for_related_field(related_field_path, related_model, color_lookup, color_lookup_field): + """ + Creates the CSS context_dict to use in a TemplateContext instance for the given ForeignKey model field + :param related_field_path: If the field is a many-to-many, specify this, e.g. 'built_form__id'. + :param related_model - Model object having items with a color_lookup_field + :param color_lookup: A dict that maps a field of the ForeignKey model class to a color + :param color_lookup_field: The field of the ForeignKey model that matches the keys of the color_lookup + :return: A complete default context_dict for the give model field + """ + + template_context_dict = null_context_dict([related_field_path]) + + for lookup_field_value, color in color_lookup.iteritems(): + if not color: + continue + try: + foreign_key = related_model.objects.get(**{color_lookup_field: lookup_field_value}).id + except: + continue + + template_context_dict['attributes'][related_field_path]['equals'][foreign_key] = { + 'fill': {'color': color}, + } + + return template_context_dict + + +def null_context_dict(styled_attributes): + """ + For the given attributes strings, returns a context_dict with only null selectors. This is the starting point + for building a context_dict. The null fill and color are hard-coded. + :param styled_attributes: + :return: an incomplete context_dict to be filled out by other functions + """ + return { + 'htmlClass': None, + 'attributes': map_to_dict(lambda styled_attribute: [ + styled_attribute, + { + 'equals': { + 'null': { + "fill": { + "color": '#f8fcff', + "opacity": .2 + }, + "outline": { + "color": "#CCCCCC" + } + }, + }, + 'greater_than': {}, + 'less_than': {} + } + ], + styled_attributes), + } + + +def load_style_templates(styled_class, styled_attribute=None): + """ + Looks for a style template based on the styled_class and styled_attribute. The latter is optional. If no + file exists for the combination, the code will search for the nonattributed style. If that fails, the code + will continue the search with each ancestor of the styled_class, with and without attribute, until a match is + found. + :param styled_class: The class that represents the table being styled + :param styled_attribute: Optional. The attribute being styled. + :return: + """ + for cls in getmro(styled_class): + if styled_attribute: + attributed_cartocss_file = settings.FOOTPRINT_TEMPLATE_DIR + "/maps/%s__%s.cartocss" % (cls.__name__, styled_attribute) + nonattributed_cartocss_file = attributed_cartocss_file.split('__')[0]+'.cartocss' + else: + attributed_cartocss_file = None + nonattributed_cartocss_file = settings.FOOTPRINT_TEMPLATE_DIR + "/maps/%s.cartocss" % cls.__name__ + + for cartocss_file in map(lambda f: f, [attributed_cartocss_file, nonattributed_cartocss_file]): + if os.path.exists(cartocss_file): + cartocss_style_template = open( + cartocss_file, + "r").read() + + css_file = cartocss_file.replace('.cartocss', '.css') + standardcss_style_template = open( + css_file, "r").read() + return dict(cartocss=cartocss_style_template, css=standardcss_style_template) + raise Exception("No style file found for style_class %s (nor its ancestors) %s %s without styled_attribute %s" % + (styled_class.__name__, + ', '.join(map(lambda cls: cls.__name__, getmro(styled_class))), + 'neither with nor' if styled_attribute else '', + styled_attribute or '')) + + +def create_style_template(template_context_dict, db_entity_key, styled_class=None, *styled_attributes): + """ + Creates a TemplateContext and Template that contains that TemplateContext for CSS styling. + :param template_context_dict: A python dict which reveals what key values can be set by the user to be + used as the context of the CSS Django templates. The dict should have default values. + :param db_entity_key. The db_entity_key that the layer represents. This is used to name the template_key + if the styled_class isn't specified + :param styled_class. The optional feature class upon which the template key is named. If this is omitted, + a generic template is created that doesn't load any predefined style info from the system. + TODO There is no particular purpose for a Template based on only a db_entity_key at this point. + The one based on a styled_class (Feature class) can load the template matching the class and attributes to + provide default styling for the layer. We might have a case for having various generic default styling for a + layer that is not based on a feature_class.:w + :return: + """ + + # Construct a template to use for Layer instances that show a geographic selection + # The template key is import, because it will be search for by Layer instances + # To find the Template instance they should use as a basis of their styling. + # The combination of the template key with an optional attribute name + # is used to match the preconfigured cartocss and css template file + # For instance, if the styled_class.__name__ is CensusBlock, that name combined + # with the attribute block will be used to find the template file CensusBlock__block.*, + # which styles CensusBlocks by their block attribute. If no attribute is relevant + # the template_key alone will be used to resolve the style file. For instance, + # CpadHoldingsFeature just styles based on its geometry, so that class name here + # will be used to find style files named CpadHoldingsFeature.* + template_key = LayerMediumKey.Fab.ricate(styled_class.__name__ if styled_class else db_entity_key) + Template.objects.update_or_create( + key=template_key, + defaults={ + 'name': template_key, + 'content_type': Keys.CONTENT_TYPE_CSS, + 'content': dict( + # Creates a dict(attribute1:dict(cartocss:a_cartocss_template, css:a_regular_css_template), ...) + attributes=map_to_dict( + lambda styled_attribute: + [styled_attribute, load_style_templates(styled_class, styled_attribute)], + styled_attributes) + ), + 'template_context': template_context_dict, + 'description': 'The tilestache CSS template for class %s' % styled_class.__name__ + }) \ No newline at end of file diff --git a/footprint/main/resources/__init__.py b/footprint/main/resources/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/resources/analytic_resources.py b/footprint/main/resources/analytic_resources.py new file mode 100644 index 000000000..70df715c7 --- /dev/null +++ b/footprint/main/resources/analytic_resources.py @@ -0,0 +1,63 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie import fields +from tastypie.constants import ALL_WITH_RELATIONS +from tastypie.fields import ToOneField +from footprint.main.models.analysis_module.vmt_module.vmt import Vmt +from footprint.main.models.analysis_module.core_module.core import Core +from footprint.main.models.analysis_module.fiscal_module.fiscal import Fiscal +from footprint.main.resources.config_entity_resources import ConfigEntityResource +from footprint.main.resources.footprint_resource import FootprintResource +from footprint.main.resources.user_resource import UserResource + +__author__ = 'calthorpe_associates' + +class AnalysisModuleResource(FootprintResource): + config_entity = ToOneField(ConfigEntityResource, attribute='config_entity', full=False, null=False) + user = fields.ToOneField(UserResource, 'user', readonly=True, full=False) + + def hydrate(self, bundle): + """ + Hydrate indicates the desire to run the AnalysisModule + :param bundle: + :return: + """ + self.user = self.resolve_user(bundle.request.GET) + if bundle.data.start: + # Start the analysis module + bundle.obj.start() + + class Meta(FootprintResource.Meta): + abstract = True + always_return_data = True + filtering = { + "config_entity": ALL_WITH_RELATIONS + } + +class CoreResource(AnalysisModuleResource): + + class Meta(AnalysisModuleResource.Meta): + abstract = False + queryset = Core.objects.all() + + +class FiscalResource(AnalysisModuleResource): + + class Meta(AnalysisModuleResource.Meta): + abstract = False + queryset = Fiscal.objects.all() + +class VmtResource(AnalysisModuleResource): + + class Meta(AnalysisModuleResource.Meta): + abstract = False + queryset = Vmt.objects.all() \ No newline at end of file diff --git a/footprint/main/resources/api.py b/footprint/main/resources/api.py new file mode 100644 index 000000000..1139ae278 --- /dev/null +++ b/footprint/main/resources/api.py @@ -0,0 +1,32 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import os +from django.conf import settings + +__author__ = 'calthorpe_associates' + +def detail_url(instance, format='json'): + """ + Given a model instance returns the base tastypie API url + :param instance: Any model instance supported by the API + :return: the string URL + """ + return os.path.join(settings.API_PATH, instance.__class__._meta.verbose_name_raw, str(instance.pk), '?format={0}'.format(format)) + +def list_url(cls, format='json'): + return os.path.join(settings.API_PATH, cls._meta.verbose_name_raw, '?format={0}'.format(format)) diff --git a/footprint/main/resources/b64field.py b/footprint/main/resources/b64field.py new file mode 100644 index 000000000..9d48d700d --- /dev/null +++ b/footprint/main/resources/b64field.py @@ -0,0 +1,78 @@ +import base64 +import os +import mimetypes + +from django.core.files.uploadedfile import SimpleUploadedFile +from tastypie import fields + +class Base64FileField(fields.FileField): + """ + A django-tastypie field for handling file-uploads through raw post data. + It uses base64 for en-/decoding the contents of the file. + Usage: + + class MyResource(ModelResource): + file_field = Base64FileField("file_field") + + class Meta: + queryset = ModelWithFileField.objects.all() + + In the case of multipart for submission, it would also pass the filename. + By using a raw post data stream, we have to pass the filename within our + file_field structure: + + file_field = { + "name": "myfile.png", + "file": "longbas64encodedstring", + "content_type": "image/png" # on hydrate optional + } + + Your file_field will by dehydrated in the above format if the return64 + keyword argument is set to True on the field, otherwise it will simply + return the URL. + """ + + def __init__(self, **kwargs): + self.return64 = kwargs.pop('return64', False) + super(Base64FileField, self).__init__(**kwargs) + + def _url(self, obj): + instance = getattr(obj, self.instance_name, None) + try: + url = getattr(instance, 'url', None) + except ValueError: + url = None + return url + + def dehydrate(self, bundle, **kwargs): + if not self.return64: + return self._url(bundle.obj) + else: + if (not self.instance_name in bundle.data + and hasattr(bundle.obj, self.instance_name)): + file_field = getattr(bundle.obj, self.instance_name) + if file_field: + content_type, encoding = mimetypes.guess_type( + file_field.file.name) + b64 = open( + file_field.file.name, "rb").read().encode("base64") + ret = {"name": os.path.basename(file_field.file.name), + "file": b64, + "content-type": (content_type or + "application/octet-stream")} + return ret + return None + + def hydrate(self, obj): + value = super(Base64FileField, self).hydrate(obj) + if value and isinstance(value, dict): + return SimpleUploadedFile(value["name"], + base64.b64decode(value["file"]), + value.get("content_type", + "application/octet-stream")) + elif isinstance(value, basestring): + if value == self._url(obj.obj): + return getattr(obj.obj, self.instance_name).name + return value + else: + return None diff --git a/footprint/main/resources/built_form_resources.py b/footprint/main/resources/built_form_resources.py new file mode 100644 index 000000000..595052703 --- /dev/null +++ b/footprint/main/resources/built_form_resources.py @@ -0,0 +1,203 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Assofrom calthorpe.footprint.resources.footprint_resource import FootprintResourceciates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie import fields + +from footprint.main.models.built_form.built_form import BuiltForm +from footprint.main.models.built_form.placetype import Placetype +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from footprint.main.models.built_form.primary_component import PrimaryComponent +from footprint.main.models.built_form.building_use_definition import BuildingUseDefinition +from footprint.main.models.built_form.building_use_percent import BuildingUsePercent +from footprint.main.models.built_form.primary_component_percent import PrimaryComponentPercent +from footprint.main.models.built_form.building_attribute_set import BuildingAttributeSet +from footprint.main.models.built_form.built_form_set import BuiltFormSet +from footprint.main.models.presentation.built_form_example import BuiltFormExample +from footprint.main.resources.medium_resources import MediumResource +from footprint.main.resources.mixins.mixins import TagResourceMixin, ToManyFieldWithSubclasses, ToManyCustomAddField +from footprint.main.resources.footprint_resource import FootprintResource +from footprint.main.resources.pickled_dict_field import PickledDictField + +from footprint.main.models import FlatBuiltForm, PlacetypeComponentPercent +from footprint.main.resources.flat_built_form_resource import FlatBuiltFormResource + +__author__ = 'calthorpe_associates' + + +class BuildingUseDefinitionResource(FootprintResource): + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = BuildingUseDefinition.objects.all() + resource_name = 'building_use_definition' + + +class BuildingUsePercentResource(FootprintResource): + + building_use_definition = fields.ToOneField(BuildingUseDefinitionResource, 'building_use_definition', full=True, null=True) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = BuildingUsePercent.objects.all() + resource_name = 'building_use_percent' + + +class BuildingAttributeSetResource(FootprintResource): + + building_uses_query = lambda bundle: bundle.obj.buildingusepercent_set.all() + building_uses = fields.ToManyField(BuildingUsePercentResource, + attribute=building_uses_query, + full=True, + null=True) + + flat_building_densities_query = lambda bundle: FlatBuiltForm.objects.get(built_form_id=bundle.obj.id) + flat_building_densities = fields.ToOneField(FlatBuiltFormResource, + attribute=flat_building_densities_query, + full=True, + null=True) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = BuildingAttributeSet.objects.all() + resource_name = 'building_attribute_set' + + +class BuiltFormExampleResource(FootprintResource): + content = PickledDictField(attribute='content', null=True, blank=True, default=lambda: {}) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = BuiltFormExample.objects.all() + resource_name = 'built_form_example' + + +class BuiltFormResource(FootprintResource, TagResourceMixin): + medium = fields.ToOneField(MediumResource, 'medium', null=True, full=False) + media = fields.ToManyField(MediumResource, 'media', null=True, full=False) + examples = fields.ToManyField(BuiltFormExampleResource, 'examples', null=True, full=False) + # TODO building_attributes on the Django model need to be renamed to building_attribute_set + building_attribute_set = fields.ToOneField(BuildingAttributeSetResource, 'building_attributes', full=False, null=True) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = BuiltForm.objects.filter(deleted=False).all().select_subclasses() + resource_name = 'built_form' + + +class BuiltFormSetResource(FootprintResource): + # These are readonly for now + built_forms = ToManyFieldWithSubclasses( + BuiltFormResource, + attribute="built_forms", + full=False, + readonly=False) + + # built_forms = ToManyField(BuiltFormResource, 'built_forms', full=True) + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = BuiltFormSet.objects.filter(deleted=False) + resource_name = 'built_form_set' + + +# class PrimaryComponentPercentPlaceTypeComponentResource(BuiltFormResource): +# +# class Meta(FootprintResource.Meta): +# always_return_data = True +# queryset = PrimaryComponentPercent.objects.all() +# resource_name = 'primary_component_percent_place_type_component' + + +class PrimaryComponentResource(BuiltFormResource): + primary_component_percent_query = lambda bundle: bundle.obj.primarycomponentpercent_set.all() + primary_component_percent_set = fields.ToManyField( + 'footprint.main.resources.built_form_resources.PrimaryComponentPercentResource', + attribute=primary_component_percent_query, + full=False, + null=True, + readonly=True) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = PrimaryComponent.objects.filter(deleted=False) + resource_name = 'primary_component' + +class PrimaryComponentPercentResource(FootprintResource): + primary_component = fields.ToOneField(PrimaryComponentResource, 'primary_component', full=False, null=True) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = PrimaryComponentPercent.objects.all() + resource_name = 'primary_component_percent' + + + +class PlacetypeComponentResource(BuiltFormResource): + # The readonly through instances that show what primary components this placetyp component contains and at what percent + primary_component_percent_query = lambda bundle: bundle.obj.primarycomponentpercent_set.all(), + @staticmethod + def add_primary_component_percents(bundle, *primary_component_percents): + for primary_component_percent in primary_component_percents: + primary_component_percent.save() + + primary_component_percents = ToManyCustomAddField( + PrimaryComponentPercentResource, + attribute=primary_component_percent_query, + add=add_primary_component_percents, + full=True, + null=True) + + # The readonly through instances that show what placetypes contain this placetype component and at what percent + placetype_component_percent_query = lambda bundle: bundle.obj.placetypecomponentpercent_set.all(), + placetype_component_percent_set = fields.ToManyField( + 'footprint.main.resources.built_form_resources.PlacetypeComponentPercentResource', + attribute=placetype_component_percent_query, + full=False, + null=True, + readonly=True) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = PlacetypeComponent.objects.filter(deleted=False) + resource_name = 'placetype_component' + + +class PlacetypeComponentPercentResource(FootprintResource): + placetype_component = fields.ToOneField(PlacetypeComponentResource, 'placetype_component', full=False, null=True) + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = PrimaryComponentPercent.objects.all() + resource_name = 'placetype_component_percent' + + +class PlacetypeResource(BuiltFormResource): + placetype_component_percent_query = lambda bundle: bundle.obj.placetypecomponentpercent_set.all() + @staticmethod + def add_placetype_component_percents(bundle, *placetype_component_percents): + for placetype_component_percent in placetype_component_percents: + placetype_component_percent.save() + placetype_component_percents = ToManyCustomAddField(PlacetypeComponentPercentResource, + attribute=placetype_component_percent_query, + add=add_placetype_component_percents, + full=False, + null=True) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = Placetype.objects.filter(deleted=False) + resource_name = 'placetype' + + diff --git a/footprint/main/resources/category_resource.py b/footprint/main/resources/category_resource.py new file mode 100644 index 000000000..8aa8c3be0 --- /dev/null +++ b/footprint/main/resources/category_resource.py @@ -0,0 +1,31 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie.resources import ModelResource +from footprint.main.models.category import Category +from footprint.main.resources.footprint_resource import FootprintResource + +__author__ = 'calthorpe_associates' + +class CategoryResource(ModelResource): + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = Category.objects.all() + filtering = { + # Accept the django query id__in + "key": ('in','exact'), + } diff --git a/footprint/main/resources/client/__init__.py b/footprint/main/resources/client/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/resources/client/client_land_use_definition_resource.py b/footprint/main/resources/client/client_land_use_definition_resource.py new file mode 100644 index 000000000..86544c977 --- /dev/null +++ b/footprint/main/resources/client/client_land_use_definition_resource.py @@ -0,0 +1,47 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.client.configuration.utils import resolve_fixture_class +from footprint.main.models import ConfigEntity +from footprint.main.models.built_form.client_land_use_definition import ClientLandUseDefinition +from footprint.main.resources.mixins.dynamic_resource import DynamicResource +from footprint.main.utils.dynamic_subclassing import get_dynamic_resource_class +from footprint import settings + + +class ClientLandUseDefinitionResource(DynamicResource): + """ + This is an abstract resource class. A client specific resource subclass is created by dynamic_resource_class + """ + + class Meta(DynamicResource.Meta): + abstract = True, + always_return_data = False + resource_name = 'client_land_use_definition' + queryset = ClientLandUseDefinition.objects.all() + + def create_subclass(self, params, **kwargs): + land_use_definition_fixture_class = resolve_fixture_class( + "built_form", + "land_use_definition", + ClientLandUseDefinition, + settings.CLIENT) + + return get_dynamic_resource_class( + self.__class__, + land_use_definition_fixture_class) + + def resolve_config_entity(self, params): + """ + :param params.config_entity: The id of the config_entity + :return: The subclassed ConfigEntity instanced based on the param value + """ + return ConfigEntity.objects.filter(id=int(params['config_entity__id'])).all().select_subclasses()[0] diff --git a/footprint/main/resources/config_entity_resources.py b/footprint/main/resources/config_entity_resources.py new file mode 100644 index 000000000..b7d64cc41 --- /dev/null +++ b/footprint/main/resources/config_entity_resources.py @@ -0,0 +1,194 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie.constants import ALL_WITH_RELATIONS, ALL +from tastypie.fields import CharField +from tastypie.resources import csrf_exempt +from footprint.main.lib.functions import get_first + +from footprint.main.models import Project, Region, GlobalConfig, Scenario, ConfigEntity, Core +from footprint.main.models.config.scenario import BaseScenario, FutureScenario +from footprint.main.resources.medium_resources import MediumResource +from footprint.main.resources.model_dict_field import ModelDictField +from footprint.main.resources.mixins.mixins import PolicySetsResourceMixin, BuiltFormSetsResourceMixin, DbEntityResourceMixin, PresentationResourceMixin, CategoryResourceMixin +from footprint.main.resources.footprint_resource import FootprintResource +from tastypie import fields +from footprint.main.utils.utils import foreign_key_field_of_related_class, has_explicit_through_class + +__author__ = 'calthorpe_associates' + + +class CustomModelDictField(ModelDictField): + def key_dehydrate_override(self): + return {'db_entities': 'db_entity_interests'} + + def instance_dehydrate_override(self): + return {'db_entity_interests': + lambda config_entity, db_entity: config_entity.dbentityinterest_set.filter(db_entity=db_entity)[0] + } + + def key_hydrate_override(self): + return {'db_entity_interests': 'db_entities'} + + def instance_hydrate_override(self): + return {'db_entities': lambda config_entity, db_entity_interest: db_entity_interest.db_entity} + + +class ConfigEntityResource(FootprintResource, PolicySetsResourceMixin, BuiltFormSetsResourceMixin, + DbEntityResourceMixin, PresentationResourceMixin, CategoryResourceMixin): + media = fields.ToManyField(MediumResource, 'media', full=False, null=True) + + # Selections needs to be revisited, so it is readonly for now + selections = CustomModelDictField(attribute='selections', null=False, blank=False, readonly=False) + # These should never be written, they are calculated automatically + schema = CharField(attribute='schema', readonly=True) + scope = CharField(attribute='scope', readonly=True) + + def hydrate(self, bundle): + """ + Set the user who created the ConfigEntity + :param bundle: + :return: + """ + if not bundle.obj.id: + bundle.obj.creator = self.resolve_user(bundle.request.GET) + bundle.obj.updater = self.resolve_user(bundle.request.GET) + return bundle + + def save_m2m(self, bundle): + """ + Overrides the super method in order to handle saving many-to-many collection instances of an explicit through class. For some reason tastypie has no handling for this, but we want to deliver the through class instances to the user that have references to the related attribute (e.g. DbEntityInterest instances are delivered and each has a reference to DbEntity). We also want to allow the client to modify, add, and remove these instances. Thus we must intercept them here and save them properly. Tastypie assumes non-explict Through classes and just dumbly tries to add them to the related field with add(), which fails for explicitly through classes. + :param bundle: + :return: + """ + + # This is an exact copy of the super method up until the add() line + for field_name, field_object in self.fields.items(): + if not getattr(field_object, 'is_m2m', False): + continue + + if not field_object.attribute: + continue + + if field_object.readonly: + continue + + # Get the manager. + related_mngr = None + + if isinstance(field_object.attribute, basestring): + related_mngr = getattr(bundle.obj, field_object.attribute) + elif callable(field_object.attribute): + related_mngr = field_object.attribute(bundle) + + if not related_mngr: + continue + + # This condition is an enhancement to thte super method. It allows an add method defined on the field to indicate how to add the many-to-many items + # We don't use this since our items are handled more carefully below + #if hasattr(related_mngr, 'clear'): + # Clear it out, just to be safe. + # related_mngr.clear() + + related_objs = [] + + # TODO handle remove and clear + if hasattr(field_object, 'add'): + # This condition is an enhancement to the super method. It allows an add method defined on the field to indicate how to add the many-to-many items + objs = map(lambda bundle: bundle.obj, bundle.data[field_name]) + field_object.add(bundle, *objs) + else: + for related_bundle in bundle.data[field_name]: + # This if statement is a change from the super method. If we are handling explict through instances we need to give the incoming instance a reference to the bundle.obj. The through instances are never dehydrated with this reference since it simply refers back to the container (bundle.data) + if has_explicit_through_class(bundle.obj, field_object.instance_name): + setattr( + related_bundle.obj, + # Figure out the correct field + foreign_key_field_of_related_class(related_bundle.obj.__class__, bundle.obj.__class__).name, + bundle.obj) + related_bundle.obj.save() + related_objs.append(related_bundle.obj) + # This if statement is a change from the super method. If we are handling explict through instances the save above is adequate. We don't want to try to add the item to the manager. + if hasattr(related_mngr, 'add'): + related_mngr.add(*related_objs) + + class Meta(FootprintResource.Meta): + abstract = True + always_return_data = True + queryset = ConfigEntity.objects.filter(deleted=False) + resource_name = 'config_entity' + filtering = { + # Accept the parent_config_entity to limit the ConfigEntity instances to a certain id + # (i.e. parent_config_entity__id=n) + "parent_config_entity": ALL_WITH_RELATIONS, + "id": ALL + } + + +class GlobalConfigResource(ConfigEntityResource): + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = GlobalConfig.objects.filter(deleted=False) + resource_name = 'global_config' + + +class RegionResource(ConfigEntityResource): + parent_config_entity = fields.ToOneField(ConfigEntityResource, 'parent_config_entity', full=False) + + class Meta(ConfigEntityResource.Meta): + always_return_data = True + queryset = Region.objects.filter(deleted=False) + resource_name = 'region' + + +class ProjectResource(ConfigEntityResource): + parent_config_entity = fields.ToOneField(RegionResource, 'parent_config_entity', full=False) + + class Meta(ConfigEntityResource.Meta): + always_return_data = True + queryset = Project.objects.filter(deleted=False) + resource_name = 'project' + + +class ScenarioResource(ConfigEntityResource): + parent_config_entity = fields.ToOneField(ProjectResource, 'parent_config_entity', full=False) + origin_config_entity = fields.ToOneField('self', 'origin_config_entity', full=False, null=True) + analysis_modules = CustomModelDictField(attribute='analysis_module', null=True, default=lambda: {}) + + class Meta(ConfigEntityResource.Meta): + abstract = False + always_return_data = True + queryset = Scenario.objects.filter(deleted=False) + resource_name = 'scenario' + + +class BaseScenarioResource(ScenarioResource): + parent_config_entity = fields.ToOneField(ProjectResource, 'parent_config_entity', full=False) + + class Meta(ConfigEntityResource.Meta): + always_return_data = True + queryset = BaseScenario.objects.filter(deleted=False) + resource_name = 'base_scenario' + + +class FutureScenarioResource(ScenarioResource): + parent_config_entity = fields.ToOneField(ProjectResource, 'parent_config_entity', full=False) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = FutureScenario.objects.filter(deleted=False) + resource_name = 'future_scenario' diff --git a/footprint/main/resources/db_entity_resources.py b/footprint/main/resources/db_entity_resources.py new file mode 100644 index 000000000..d450ff04e --- /dev/null +++ b/footprint/main/resources/db_entity_resources.py @@ -0,0 +1,100 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.conf.urls import url + +from tastypie import fields +from tastypie.bundle import Bundle +from tastypie.fields import ListField, ToOneField +from tastypie.resources import ModelResource +from footprint.main.models.config.db_entity_interest import DbEntityInterest +from footprint.main.models.geospatial.db_entity import DbEntity +from footprint.main.models.config.interest import Interest +from footprint.main.resources.config_entity_resources import ConfigEntityResource +from footprint.main.resources.mixins.mixins import TagResourceMixin +from footprint.main.resources.footprint_resource import FootprintResource +from footprint.main.resources.pickled_dict_field import PickledDictField + +__author__ = 'calthorpe_associates' + +class DbEntityResource(FootprintResource, TagResourceMixin): + hosts = fields.ListField('hosts', null=True) + + # This gets sent by the client and is used to set the url. + # It is marked readonly so that tastypie doesn't try to find a matching + # DbEntity using it. I don't know how to tell tastypie to just map this + # value to url + upload_id = fields.CharField(null=True, readonly=True) + origin_instance = ToOneField('self', attribute='origin_instance', null=True) + + def hydrate(self, bundle): + if not bundle.obj.id: + bundle.obj.creator = self.resolve_user(bundle.request.GET) + bundle.obj.updater = self.resolve_user(bundle.request.GET) + return bundle + + def hydrate_url(self, bundle): + # Use the upload_id to create a source url for the db_entity + if bundle.data.get('upload_id', False): + bundle.data['url'] = 'file:///tmp/%s' % bundle.data['upload_id'] + return bundle + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = DbEntity.objects.filter(deleted=False) + excludes=['feature_class_configuration'] + resource_name= 'db_entity' + +class InterestResource(ModelResource): + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = Interest.objects.all() + +class DbEntityInterestResource(ModelResource): + config_entity = fields.ToOneField(ConfigEntityResource, 'config_entity', full=False) + db_entity = fields.ToOneField(DbEntityResource, 'db_entity', full=True) + interest = fields.ToOneField(InterestResource, 'interest', full=True) + + # The fields of the DbEntity's feature class, if one exists + feature_fields = ListField(attribute='feature_fields', null=True, blank=True, readonly=True) + feature_field_title_lookup = PickledDictField(attribute='feature_field_title_lookup', null=True, blank=True, readonly=True) + + + def dehydrate_interest(self, bundle): + """ + Expose only the Interest.key via the API. This should be sufficient for the time being + :param bundle: + :return: + """ + return bundle.data['interest'].data['key'] + + def hydrate_interest(self, bundle): + """ + Expect only the key of Interest in as the bundle.data['interest'] + :param bundle: + :return: + """ + interest_resource = InterestResource() + if not isinstance(bundle.data['interest'], Bundle): + # For some reason the interest sometimes comes in as a bundle, at least in testing + interest = Interest.objects.get(key=bundle.data['interest']) + bundle.data['interest'] = interest_resource.full_dehydrate(interest_resource.build_bundle(obj=interest)) + return bundle + + class Meta(FootprintResource.Meta): + queryset = DbEntityInterest.objects.filter(deleted=False) + resource_name= 'db_entity_interest' diff --git a/footprint/main/resources/feature_resources.py b/footprint/main/resources/feature_resources.py new file mode 100644 index 000000000..8e156b3ec --- /dev/null +++ b/footprint/main/resources/feature_resources.py @@ -0,0 +1,131 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db import models +from inflection import singularize + +from tastypie import fields +from tastypie.models import ApiKey +from footprint.main.lib.functions import map_dict_to_dict, map_dict_value +from footprint.main.models import Layer +from footprint.main.models.presentation.layer_selection import get_or_create_dynamic_layer_selection_class_and_table +from footprint.main.resources.config_entity_resources import ConfigEntityResource +from footprint.main.resources.footprint_resource import FootprintResource +from footprint.main.resources.mixins.dynamic_resource import DynamicResource +from footprint.main.utils.dynamic_subclassing import get_dynamic_resource_class +from footprint.main.utils.subclasses import match_subclasses +from footprint.main.utils.utils import get_one_or +from tastypie.constants import ALL_WITH_RELATIONS, ALL +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator + +class FeatureResource(DynamicResource): + # Dehydrate the class config_entity into each instance. This is useful for Api consumers since the + # feature id alone is not distinct when using an abstract Feature class + # readonly=True so that we never hydrate this, which takes a long time. We always resolve the config entity by id + # in create_subclass below. + config_entity = fields.ToOneField(ConfigEntityResource, 'config_entity', full=False, readonly=True) + + def resolve_feature_class(self, config_entity, layer): + return config_entity.feature_class_of_db_entity_key(layer.db_entity_key) if \ + layer else \ + config_entity.feature_class_of_base_class(self._meta.queryset.model) + + def create_subclass(self, params, **kwargs): + """ + Subclass this class to create a resource class specific to the config_entity. + :param params.layer__id: The layer id. Optional. Used to resolve the Feature/FeatureResource subclasses if we are in FeatureResource (not in a subclass) + :return: The subclassed resource class + """ + + # Get the ConfigEntity subclass instance based on the parameter + config_entity = self.resolve_config_entity(params) + layer = params.get('layer__id', None) and self.resolve_layer(params) + db_entity = layer.db_entity_interest.db_entity if layer else None + + # Use the abstract resource class queryset model or given db_entity_key to fetch the feature subclass + feature_class = self.resolve_feature_class(config_entity, layer) + return self.resolve_feature_resource_class(feature_class, db_entity) + + def resolve_feature_resource_class(self, feature_class, db_entity=None): + """ + Resolve the FeatureResource subclass based on the given Feature subclass + If self is already a subclass, just return self + Else, return a preconfigured subclass or one dynamically created. The latter will probably be the only way in the future. + :param db_entity: Optionally passed to configure a dynamic FeatureResource subclass + :return: An instance + """ + # If not already subclassed + if self.__class__ == FeatureResource: + descriptors = FeatureClassCreator(feature_class.config_entity, db_entity).related_descriptors() if db_entity else {} + return self.__class__.resolve_resource_class(feature_class, descriptors) + return self + + def dynamic_resource_class(self, params, feature_class): + return get_dynamic_resource_class(self.__class__, feature_class) + + def search_params(self, params): + """ + The user may optionally specify a layer_selection__id instead of feature ids when querying for features. + This prevents huge feature id lists in the URL. + :param params + :return: + """ + if params.get('layer__id'): + layer_selection = self.resolve_layer_selection(params) + return dict(id__in=','.join(map(lambda feature: unicode(feature.id), layer_selection.selected_features))) + else: + return params + + def remove_params(self, params): + """ + layer_selection__id is converted to id__in for feature ids. The former must be removed during the + wrapping of the resource if footprint_resource + :param params: + :return: + """ + return ['layer_selection__id', 'config_entity__id', 'layer__id'] + + def resolve_config_entity(self, params): + if params.get('config_entity__id', None): + return super(FeatureResource, self).resolve_config_entity(params) + elif params.get('layer__id', None): + layer = self.resolve_layer(params) + # This hack gets us the right ConfigEntity subclass version of the instance + return super(FeatureResource, self).resolve_config_entity(dict(config_entity__id=layer.presentation.config_entity.id)) + else: + raise Exception("Expected config_entity__id or layer__id to be specified") + + def resolve_layer(self, params): + return Layer.objects.get(id=params['layer__id']) + + def resolve_layer_selection(self, params): + layer = self.resolve_layer(params) + layer_selection_class = get_or_create_dynamic_layer_selection_class_and_table(layer, False) + return layer_selection_class.objects.get(user=self.resolve_user(params)) + + def post_save(self, request, **kwargs): + params = request.GET + user_id = ApiKey.objects.get(key=params['api_key']).user_id + feature_class = self._meta.queryset.model + feature_class.post_save(user_id) + + class Meta(DynamicResource.Meta): + always_return_data = True + abstract = True + #fields = ['id'] + excludes = ['created', 'wkb_geometry'] + filtering = { + # Accept the django query id__in + "id": ALL + } + resource_name = 'feature' + + diff --git a/footprint/main/resources/flat_built_form_resource.py b/footprint/main/resources/flat_built_form_resource.py new file mode 100644 index 000000000..73d6b34bd --- /dev/null +++ b/footprint/main/resources/flat_built_form_resource.py @@ -0,0 +1,14 @@ +from footprint.main.models import FlatBuiltForm +from footprint.main.resources.footprint_resource import FootprintResource + +__author__ = 'calthorpe_associates' + +class FlatBuiltFormResource(FootprintResource): + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = FlatBuiltForm.objects.all() + resource_name = 'flat_built_form' + filtering = { + "built_form_id": ('exact',), + } diff --git a/footprint/main/resources/footprint_resource.py b/footprint/main/resources/footprint_resource.py new file mode 100644 index 000000000..a24d0df7d --- /dev/null +++ b/footprint/main/resources/footprint_resource.py @@ -0,0 +1,175 @@ +import logging +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError +from django.db import models +from inflection import singularize +from tastypie.authentication import ApiKeyAuthentication +from tastypie.exceptions import BadRequest +from tastypie.authorization import DjangoAuthorization +from tastypie import fields +from tastypie.contrib.gis.resources import ModelResource +from tastypie.http import HttpBadRequest +from tastypie.resources import csrf_exempt +import traceback +from footprint.main.lib.functions import map_dict_to_dict, merge +from footprint.main.utils.dynamic_subclassing import get_dynamic_resource_class +from footprint.main.utils.subclasses import match_subclasses +from footprint.main.utils.utils import get_one_or, clear_many_cache +import json + +__author__ = 'calthorpe_associates' + +class FootprintResource(ModelResource): + """ + Adds django revision with the tastypie ModelResource + """ + def __init__(self): + # Get an instance of a logger + self.logger = logging.getLogger(__name__) + super(FootprintResource, self).__init__() + + class Meta(object): + authentication = ApiKeyAuthentication() + authorization = DjangoAuthorization() + + # From http://django-tastypie.readthedocs.org/en/latest/cookbook.html + def dispatch(self, request_type, request, **kwargs): + format = request.META.get('CONTENT_TYPE', 'application/json') + # We only can log the raw_post_data if the request isn't multipart + self.logger.debug( + '%s %s %s' % + (request.method, request.get_full_path(), + request.raw_post_data if not format.startswith('multipart') else '...MULTIPART...' + ) + ) + + exception = None + try: + response = super(ModelResource, self).dispatch( + request_type, request, **kwargs) + + if request.method in ['POST', 'PATCH']: + self.post_save(request, **kwargs) + except (BadRequest, fields.ApiFieldError), e: + exception = e + self.logger.debug( + 'Response 400 %s' % e.args[0]) + raise + except ValidationError, e: + exception = e + self.logger.debug( + 'Response 400 %s' % e.messages) + raise + except Exception, e: + exception = e + if hasattr(e, 'response'): + self.logger.debug( + 'Response %s %s %s' % + (e.response.status_code, e.response.content, traceback.format_exc())) + else: + self.logger.debug('Response 500: %s' % e.message) + raise + # finally: + # if exception: + # bundle = dict(code=777, status=False, error=json.loads( + # merge(dict(content=exception.response.content) if hasattr(exception, 'response') else {}, dict(message=exception.message)))) + # return self.create_response(request, bundle, response_class=HttpBadRequest) + + self.logger.debug( + 'Response %s %s' % (response.status_code, response.content)) + return response + + def wrap_view(self, view): + """ + Overrides wrap_view to allow processing of dynamic resource classes. Special parameters such as config_entity_id are used to resolve the correct resource subclass. + :param view: + :return: + """ + @csrf_exempt + def wrapper(request, *args, **kwargs): + # Dynamic resources based on a ConfigEntity instance need to pass the config_entity__id so that we can properly construct the dynamic resource + if hasattr(self.Meta, 'abstract') and self.Meta.abstract: + wrapped_view = self.subclass_resource_if_needed(view, request) + # Preserve the original request parameters + kwargs['GET'] = request.GET + request.GET = request._filters if hasattr(request, '_filters') and len(request._filters.keys()) > 0 else request.GET + else: + wrapped_view = super(ModelResource, self).wrap_view(view) + + return wrapped_view(request, *args, **kwargs) + return wrapper + + def subclass_resource_if_needed(self, view, request): + """ + Called to dynamically subclass abstract resources that wrap dynamic model classes, such as abstract subclasses of Feature. The subclassing happens if the current resource class (self) is a subclass of DynamicResource and self is abstract, the latter needed to prevent infinite recursion. The subclassed resource wraps the view and thus becomes the resource executing the request + + :param request: The request with certain required parameters in GET. Important parameters are config_entity, which is a ConfigEntity id and layer, which is a Layer id. If a ConfigEntity id is given an the resource is a FeatureResource subclass, then a ConfigEntity-specific dynamic subclass is found + :return: The wrapped view of the dynamic resource class instance or simply the wrapped view of self if no dynamic subclassing is needed. + """ + + # By default don't subclass, just call the super class wrap_view on the existing view + return super(ModelResource, self).wrap_view(view) + + def post_save(self, request, **kwargs): + """ + Optional operations to perform after POST, PUT, and PATCH + :param request: + :return: + """ + pass + + def resolve_user(self, params): + return User.objects.get(username=params['username']) + + + @classmethod + def resolve_resource_class(cls, model_class, related_descriptors={}): + """ + Match the feature_class to an existing resource class by iterating through subclasses of self.__class__ + If no match occurs generate one if db_entity is specified, else None + :param model_class: + :param related_descriptors: Optionally provided a dictionary of related descriptors of the model_class to use to create related resource fields + in the case of dynamic resource creation + :return: The matching or created resource + """ + resources = FootprintResource.match_existing_resources(model_class) + if len(resources) > 1: + logging.warn("Too many resources for model class: %s. Resources; %s" % (model_class, resources)) + resource = resources[0] if len(resources) > 0 else None + return resource or \ + get_dynamic_resource_class( + cls, + model_class, + fields=map_dict_to_dict( + lambda related_field_name, related_descriptor: cls.related_resource_field(related_field_name, related_descriptor), + related_descriptors)) + + @classmethod + def related_resource_field(cls, related_field_name, related_descriptor): + """ + Returns a tuple of the related_field resource name and resource field. ManyToMany and ForeignKey is currently supported + :param related_field_name: The related name on the model class + :param related_descriptor: The related Field on the model class. Expected is an instance of models.ManyToMany, or models.ForeignKey + :return: A tuple with the field.name or singularized version and the created resource field. + All fields created have full=False and null=True with the assumption that the value is optional and a need not be nested + """ + + related_field = related_descriptor.field + related_resource_class = FootprintResource.resolve_resource_class(related_field.rel.to) + # Clear the related object field caches in case it didn't pick up the dynamic many-to-many field of cls's model + clear_many_cache(related_field.rel.to) + if isinstance(related_field, models.ManyToManyField): + return [related_field.name, fields.ToManyField(related_resource_class, related_field.name, full=False, null=True)] + if isinstance(related_field, models.ForeignKey): + return [related_field.name, fields.ToOneField(related_resource_class, related_field.name, full=False, null=True)] + + @classmethod + def match_existing_resources(cls, model_class): + """ + Find all subclasses of self.__class__ whoses queryset model is the given model_class) + :param model_class: The model class to match + :return: + """ + return match_subclasses( + cls, + lambda resource_class: not (hasattr(resource_class._meta, 'abstract') and resource_class._meta.abstract) and resource_class._meta.queryset and issubclass(model_class, resource_class._meta.queryset.model)) diff --git a/footprint/main/resources/geography_resource.py b/footprint/main/resources/geography_resource.py new file mode 100644 index 000000000..9423137ec --- /dev/null +++ b/footprint/main/resources/geography_resource.py @@ -0,0 +1,18 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models import Geography +from footprint.main.resources.footprint_resource import FootprintResource + +class GeographyResource(FootprintResource): + class Meta(FootprintResource.Meta): + queryset = Geography.objects.all() + excludes = ['geometry'] diff --git a/footprint/main/resources/layer_resources.py b/footprint/main/resources/layer_resources.py new file mode 100644 index 000000000..15427622c --- /dev/null +++ b/footprint/main/resources/layer_resources.py @@ -0,0 +1,51 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. # +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie.fields import ToOneField, BooleanField +from footprint.main.models import Layer, LayerLibrary +from footprint.main.resources.mixins.mixins import ToManyFieldWithSubclasses +from footprint.main.resources.presentation_medium_resource import PresentationMediumResource +from footprint.main.resources.presentation_resources import PresentationResource + +class LayerLibraryResource(PresentationResource): + + layers = ToManyFieldWithSubclasses( + 'footprint.main.resources.layer_resources.LayerResource', + attribute='layers', + full=False, + null=True) + + class Meta(PresentationResource.Meta): + resource_name = 'layer_library' + always_return_data = True + queryset = LayerLibrary.objects.all() + excludes = ['presentation_media'] + +class LayerResource(PresentationMediumResource): + + origin_instance = ToOneField('self', attribute='origin_instance', null=True) + create_from_selection = BooleanField(attribute='create_from_selection') + + def full_hydrate(self, bundle): + bundle = super(LayerResource, self).full_hydrate(bundle) + if bundle.obj.create_from_selection: + feature_class_configuration = bundle.obj.db_entity_interest.db_entity.feature_class_configuration = bundle.obj.db_entity_interest.db_entity.feature_class_configuration or {} + feature_class_configuration['source_from_origin_layer_selection'] = True + return bundle + + class Meta(PresentationMediumResource.Meta): + resource_name = 'layer' + always_return_data = True + queryset = Layer.objects.all() + filtering = { + "id": ('exact',), + } + + diff --git a/footprint/main/resources/layer_selection_resource.py b/footprint/main/resources/layer_selection_resource.py new file mode 100644 index 000000000..85959ab6c --- /dev/null +++ b/footprint/main/resources/layer_selection_resource.py @@ -0,0 +1,217 @@ +from tastypie.contrib.gis.resources import GeometryApiField +from footprint.main.resources.layer_resources import LayerResource + +__author__ = 'calthorpe' + +from django.contrib.auth.models import User +import geojson +from tastypie import fields +from tastypie.constants import ALL_WITH_RELATIONS, ALL +from footprint.main.lib.functions import deep_merge +from footprint.main.models.config.scenario import FutureScenario +from footprint.main.models.geospatial.db_entity_configuration import create_db_entity_configuration +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.main.resources.pickled_dict_field import PickledDictField +from footprint.main.models import Layer +from footprint.main.models.presentation.layer_selection import LayerSelection, get_or_create_dynamic_layer_selection_class_and_table +from footprint.main.resources.feature_resources import FeatureResource +from footprint.main.resources.mixins.dynamic_resource import DynamicResource +from footprint.main.resources.mixins.mixins import ToManyFieldWithSubclasses +from footprint.main.resources.user_resource import UserResource +from footprint.main.utils.dynamic_subclassing import get_dynamic_resource_class + +class CustomGeometryApiField(GeometryApiField): + def hydrate(self, bundle): + return super(GeometryApiField, self).hydrate(bundle) + # I don't know why the base class does this + #if value is None: + # return value + #return simplejson.dumps(value) + +class LayerSelectionResource(DynamicResource): + """ + An abstract resource class that is subclassed by the resources.py wrapper to match a particular layer_id + """ + + bounds = CustomGeometryApiField(attribute='bounds', null=True, blank=True, default=lambda:{}) + + unique_id = fields.CharField(attribute='unique_id', null=False, readonly=True) + # The layer instance is not a LayerSelection field, but a property of the LayerSelection subclass + user = fields.ToOneField(UserResource, 'user', readonly=True, full=False) + filter = PickledDictField(attribute='filter', null=True, blank=True) + group_bys = PickledDictField(attribute='group_bys', null=True, blank=True) + joins = PickledDictField(attribute='joins', null=True, blank=True) + aggregates = PickledDictField(attribute='aggregates', null=True, blank=True) + + result_fields = fields.ListField(attribute='result_fields', null=True, blank=True, readonly=True) + result_field_title_lookup = PickledDictField(attribute='result_field_title_lookup', null=True, blank=True, readonly=True) + + summary_results = PickledDictField(attribute='summary_results', null=True, blank=True, readonly=True) + summary_fields = fields.ListField(attribute='summary_fields', null=True, blank=True, readonly=True) + summary_field_title_lookup = PickledDictField(attribute='summary_field_title_lookup', null=True, blank=True, readonly=True) + + query_strings = PickledDictField(attribute='query_strings', null=True, blank=True, + default=lambda: dict(aggregates_string=None, filter_string=None, group_by_string=None)) + + query_sql = fields.CharField(attribute='query_sql', null=True, readonly=True) + summary_query_sql = fields.CharField(attribute='summary_query_sql', null=True, readonly=True) + + # TODO remove + filter_by_selection = fields.BooleanField(attribute='filter_by_selection', default=False) + + layer_lambda = lambda bundle: bundle.obj.__class__.layer + selection_layer = fields.ToOneField(LayerResource, attribute=layer_lambda, readonly=True, full=False) + + selection_extent = CustomGeometryApiField(attribute='selection_extent', null=True, blank=True, default=lambda:{}, readonly=True) + + def full_hydrate(self, bundle, for_list=False): + """ + Clear the previous bounds or query if the other is sent + :param bundle: + :return: + """ + + # TODO Not used. Remove. Store the old versions of the objects to detect changes + previous_attributes = dict( + joins=bundle.obj.joins, + query_strings=dict( + filter_string=bundle.obj.query_strings.get('filter_string', None), + aggregates_string=bundle.obj.query_strings.get('aggregates_string', None), + group_by_string=bundle.obj.query_strings.get('group_by_string', None), + ) + ) + # Remove the computed properties. Some or all will be set + bundle.obj.summary_results = None + bundle.obj.summary_fields = None + bundle.obj.summary_field_title_lookup = None + + # Call super to populate the bundle.obj + bundle = super(LayerSelectionResource, self).full_hydrate(bundle) + for attr in ['filter', 'aggregates', 'group_bys', 'joins', 'bounds']: + if not bundle.data.get(attr, None): + setattr(bundle.obj, attr, None) + + feature_class = bundle.obj.feature_class() + query_set = bundle.obj.create_query_set(feature_class.objects, previous_attributes) + # Update the features based on the new query_set + if query_set: + bundle.obj.update_features(query_set) + else: + bundle.obj.clear_features() + # Parse the QuerySet to get the result fields and their column title lookup + bundle.obj.result_fields, bundle.obj.result_field_title_lookup = (query_set if query_set!=None else feature_class.objects.all()).result_fields_and_title_lookup() + + # Create the summary results from the entire set + summary_query_set = bundle.obj.create_summary_query_set( + feature_class.objects, + previous_attributes) + if summary_query_set: + # Update the summary results + bundle.obj.update_summary_results(summary_query_set) + else: + bundle.obj.clear_summary_results() + + bundle.obj.query_sql = query_set.query if query_set else None + bundle.obj.summary_query_sql = summary_query_set.query if hasattr(summary_query_set, 'query') else None + + return bundle + + def query_data_specified(self, data): + return data.get('query', None) + + def create_subclass(self, params, **kwargs): + """ + Subclasses the LayerSelectionResource instance's class for the given config_entity. + This resource class can the return all LayerSelection instances for the given config_entity scope + :param params.config_entity + :return: + """ + + layer = self.resolve_layer(params) + layer_selection_class = get_or_create_dynamic_layer_selection_class_and_table(layer) + if not layer_selection_class: + raise Exception("Layer with db_entity_key %s has no feature_class. Its LayerSelections should not be requested" % layer.db_entity_key) + if kwargs.get('method', None) == 'PATCH': + # Create a simple Feature resource subclass (no related fields needed) + feature_resource_class = FeatureResource().create_subclass(params) + features = fields.ToManyField(feature_resource_class, attribute='selected_features', readonly=True, null=True, full=False) + else: + features = ToManyFieldWithSubclasses( + 'footprint.main.resources.feature_resources.FeatureResource', + attribute='selected_features', + full=False, + readonly=True, + null=True) + # Create a subclass of FeatureResource to wrap the Feature class of that represents the given db_entity_key + return get_dynamic_resource_class( + self.__class__, + layer_selection_class, + fields=dict(features=features) + ) + + def search_params(self, params): + """ + :param params + :return: + """ + user = User.objects.get(username=params['username']) + return dict(user__id=user.id) + + def post_save(self, request, **kwargs): + """ + Call the layer publisher on save manually since the signaling doesn't seem to work with dynamic + classes + :param request: + :return: + """ + layer_instance = self.resolve_layer(kwargs['GET']) + user = User.objects.get(username=request.GET['username']) + #layer_publishing.on_layer_selection_post_save_layer(self, layer=layer_instance, user=user) + + def resolve_config_entity(self, params): + return Layer.objects.get(id=params['layer__id']).presentation.config_entity + + def create_layer_from_layer_selection(self, params): + # Resolve the source layer from the layer_selection__id + source_layer = self.resolve_layer(params) + config_entity = source_layer.presentation.config_entity + db_entity = source_layer.db_entity_interest.db_enitty + feature_class = FeatureClassCreator(config_entity, db_entity).dynamic_feature_class() + layer = Layer.objects.get(presentation__config_entity=config_entity, db_entity_key=db_entity.key) + layer_selection = get_or_create_dynamic_layer_selection_class_and_table(layer, False).objects.all()[0] + # TODO no need to do geojson here + feature_dict = dict( + type="Feature" + ) + feature_dicts = map(lambda feature: + deep_merge(feature_dict, {"geometry":geojson.loads(feature.wkb_geometry.json)}), + layer_selection.selected_features or feature_class.objects.all()) + json = dict({ "type": "FeatureCollection", + "features": feature_dicts + }) + db_entity_configuration = create_db_entity_configuration(config_entity, **dict( + class_scope=FutureScenario, + name='Import From Selection Test', + key='import_selection_test', + url='file://notusingthis' + )) + self.make_geojson_db_entity(config_entity, db_entity_configuration, data=json) + + class Meta(DynamicResource.Meta): + abstract=True + filtering = { + # Accept the django query layer and user ids to identify the Layer and User + # layer_id is used to resolve the dynamic subclass for PATCH + #"layer": ALL_WITH_RELATIONS, + + # There is only one instance per user_id. This should always be specified for GETs + "user": ALL_WITH_RELATIONS, + "id": ALL + } + always_return_data = True + # We don't want to deliver this, the user only sees and manipulates the bounds + excludes = ['geometry'] + resource_name = 'layer_selection' + # The following is set in the subclass based upon the dynamic model class passed into the class creator + queryset = LayerSelection.objects.all() # Just for model_class initialization, should never be called + diff --git a/footprint/main/resources/medium_resources.py b/footprint/main/resources/medium_resources.py new file mode 100644 index 000000000..ded818796 --- /dev/null +++ b/footprint/main/resources/medium_resources.py @@ -0,0 +1,34 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.lib.functions import remove_keys +from footprint.main.models import Medium +from footprint.main.resources.pickled_dict_field import PickledDictField +from footprint.main.resources.footprint_resource import FootprintResource + +__author__ = 'calthorpe_associates' + +class MediumResource(FootprintResource): + content = PickledDictField(attribute='content', null=True, blank=True, default=lambda:{}) + + # def dehydrate_content(self, bundle): + # # Remove data that isn't needed by the API + # return remove_keys(bundle.data['content'], ['attributes']) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = Medium.objects.all() diff --git a/footprint/main/resources/mixins/__init__.py b/footprint/main/resources/mixins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/resources/mixins/dynamic_resource.py b/footprint/main/resources/mixins/dynamic_resource.py new file mode 100644 index 000000000..2276527fb --- /dev/null +++ b/footprint/main/resources/mixins/dynamic_resource.py @@ -0,0 +1,118 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +# Mixin that marks a resource class as needing dynamic subclassing because the model class it models needs dynamic subclassing + +from django.contrib.auth.models import Permission +from django.contrib.contenttypes.models import ContentType +from footprint.main.lib.functions import remove_keys, merge +from footprint.main.models import ConfigEntity, Layer +from footprint.main.models.config.scenario import FutureScenario, BaseScenario +from footprint.main.resources.footprint_resource import FootprintResource + +class DynamicResource(FootprintResource): + + class Meta(FootprintResource.Meta): + pass + + def create_subclass(self, params, **kwargs): + """ + Returns a subclass of self based on the given parameters. For example a BaseFeatureResource will subclass + itself based on the value of the 'config_entity_id' in params + :param params: + :return: + """ + raise Exception("Must implement create_subclass in mixer") + + def resolve_config_entity(self, params): + """ + Resolves the config_entity based on the layer id indicated in the param + :param params: + :return: + """ + + # TODO hack to handle lack of multi-level subclass relation resolution + scenarios = FutureScenario.objects.filter(id=int(params['config_entity__id'])) + if len(list(scenarios)) > 0: + # Scenario subclass instance + return scenarios[0] + else: + scenarios = BaseScenario.objects.filter(id=int(params['config_entity__id'])) + if len(list(scenarios)) > 0: + return scenarios[0] + else: + return ConfigEntity.objects.filter(id=int(params['config_entity__id'])).all().select_subclasses()[0] + + def resolve_layer(self, params): + """ + The Layer id is used to resolve the type of Feature (via its DbEntity) + :param params: + :return: + """ + return Layer.objects.get(id=params['layer__id']) + + def subclass_resource_if_needed(self, view, request): + """ + Overrides the FootprintResource method to perform subclassing of the resource based on the request params + :param view: + :param request: + :return: + """ + params = request.GET + # TODO cache dynamic class creation results + # Create the dynamic resource class + dynamic_resource_class = self.create_subclass(params, method=request.method) + config_entity = self.resolve_config_entity(params) + # This might not be need anymore, but it indicates what other dynamic classes were created so that + # permissions can be added for them + additional_classes_used = [] + # We add permissions to the current user so they can access these dynamic classes if it's the first access by the user + # TODO permissions would ideally be done ahead of time, of if we could automatically give the user full access to all. This might be fixed in the latest Django version + # subclasses of a certain type, but I don't see how to do that in the Django docs + user = self.resolve_user(params) + self.add_permissions_to_user(user, self.get_or_create_permissions_for_class(dynamic_resource_class, additional_classes_used, config_entity)) + + # Extract and add GET parameters + request._config_entity = config_entity + request._filters = remove_keys( + merge(request.GET, self.search_params(params)), + self.remove_params(params)) + + return dynamic_resource_class().wrap_view(view) + + def search_params(self, params): + """ + :param params + :return: return the modified params_copy + """ + return {} + + def remove_params(self, params): + """ + :return: a string list of parameters to remove + """ + return [] + + def get_or_create_permissions_for_class(self, DynamicResourceClass, additional_classes_used, config_entity): + model_class = DynamicResourceClass.Meta.object_class + for clazz in [model_class] + additional_classes_used: + return map(lambda action: Permission.objects.get_or_create( + codename='{0}_{1}'.format(action, clazz.__name__.lower()), + # Since our dynamic model doesn't have a ContentType, borrow that of the ConfigEntity + content_type_id=ContentType.objects.get(app_label="main", model=config_entity.__class__.__name__.lower()).id, + name='Can {0} {1}'.format(action, clazz.__name__))[0], + ['add', 'change', 'delete']) + + def add_permissions_to_user(self, user , permissions): + existing = user.user_permissions.all() + new_permissions = filter(lambda permission: permission not in existing, permissions) + user.user_permissions.add(*new_permissions) diff --git a/footprint/main/resources/mixins/mixins.py b/footprint/main/resources/mixins/mixins.py new file mode 100644 index 000000000..2a3b22f7f --- /dev/null +++ b/footprint/main/resources/mixins/mixins.py @@ -0,0 +1,176 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie.fields import ToManyField, NOT_PROVIDED +from tastypie.resources import ModelResource +from footprint.main.lib.functions import map_to_keyed_collections, flatten, map_to_dict, merge, unique, flat_map_values, get_first +from footprint.main.models import ResultLibrary, LayerLibrary +from footprint.main.resources.category_resource import CategoryResource +from footprint.main.resources.policy_resources import PolicySetResource +import logging +from footprint.main.resources.tag_resource import TagResource + +__author__ = 'calthorpe_associates' + +class ToManyCustomAddField(ToManyField): + """ + Adds the ability to place an add attribute on the field definition. The add attribute points to a lambda that specifies how to add many-to-many items to a collection. The default Tastypie method just clears the collection and adds all the items, which doesn't work for explicit through class m2ms or our RelatedCollectionAdoption sets. The add lambda accepts the bundle, which should have a reference to the fully hydrated base object at bundle.obj. + """ + + def __init__(self, to, attribute, related_name=None, default=NOT_PROVIDED, + null=False, blank=False, readonly=False, full=False, + unique=False, help_text=None, add=None): + super(ToManyCustomAddField, self).__init__( + to, attribute, related_name=related_name, default=default, + null=null, blank=blank, readonly=readonly, full=full, + unique=unique, help_text=help_text + ) + self.add = add + + +class SubclassRelatedResourceMixin(object): + """ + Mixin that allows related items to be subclasses by fetching the resource subclass instead of the base resource + Make sure that the query is actually returning subclasses by using select_subclasses() + """ + def get_related_resource(self, related_instance): + """ + Instantiates the related resource. Override this method to subclass according to the related instance, rather + than just using the to class of the Field + """ + related_resource_class = get_first(filter(lambda resource_class: issubclass(related_instance.__class__, resource_class._meta.object_class), + self.to_class().__class__.__subclasses__()), None) + related_resource = related_resource_class() if related_resource_class else self.to_class() + + if related_resource._meta.api_name is None: + if self._resource and not self._resource._meta.api_name is None: + related_resource._meta.api_name = self._resource._meta.api_name + + # Try to be efficient about DB queries. + related_resource.instance = related_instance + return related_resource + +class ToManyFieldWithSubclasses(SubclassRelatedResourceMixin, ToManyField): + """ + Mixes in the ability to subclass items + """ + pass + +class ToManyCustomAddFieldWithSubclasses(SubclassRelatedResourceMixin, ToManyCustomAddField): + """ + Mixes in the ability to subclass items + """ + pass + +class BuiltFormSetsResourceMixin(ModelResource): + built_from_sets_query = lambda bundle: bundle.obj.computed_built_form_sets() + add_built_form_sets = lambda bundle, *built_form_sets: bundle.obj.add_built_form_sets(*built_form_sets) + built_form_sets = ToManyCustomAddField('footprint.main.resources.built_form_resources.BuiltFormSetResource', attribute=built_from_sets_query, add=add_built_form_sets, full=False, null=True) + +class PolicySetsResourceMixin(ModelResource): + policy_sets_query = lambda bundle: bundle.obj.computed_policy_sets() + add_policy_sets = lambda bundle, *policy_sets: bundle.obj.add_policy_sets(*policy_sets) + policy_sets = ToManyCustomAddField(PolicySetResource, attribute=policy_sets_query, add=add_policy_sets, full=False, null=True) + +class DbEntityResourceMixin(ModelResource): + db_entity_interests_query = lambda bundle: bundle.obj.computed_db_entity_interests() + add_db_entity_interests = lambda bundle, *db_entity_interests: bundle.obj.add_db_entity_interests(*db_entity_interests) + db_entity_interests = ToManyCustomAddField('footprint.main.resources.db_entity_resources.DbEntityInterestResource', attribute=db_entity_interests_query, add=add_db_entity_interests, full=False, null=True) + +class PresentationResourceMixin(ModelResource): + # Select the subclasses since we divide up Presentations by their subclass to help the API user and Sproutcore + presentations_query = lambda bundle: bundle.obj.presentation_set.all().select_subclasses() + # Read-only subclassed presentations + presentations = ToManyFieldWithSubclasses('footprint.main.resources.presentation_resources.PresentationResource', attribute=presentations_query, full=True, null=True, readonly=True) + + def map_uri_to_class_key(self, instances_by_id, uri): + instance = instances_by_id[uri.split('/')[-2]] + if isinstance(instance, ResultLibrary): + return 'results' + if isinstance(instance, LayerLibrary): + return 'layers' + else: + raise Exception("Unknown Presentation class {0}".format(instance.__class__.__name__())) + + def map_instance_to_class_key(self, instance): + if isinstance(instance, ResultLibrary): + return 'results' + if isinstance(instance, LayerLibrary): + return 'layers' + else: + raise Exception("Unknown Presentation class {0}".format(instance.__class__.__name__())) + + def dehydrate_presentations(self, bundle): + """ + Separates the presentations by type into a dict to make them easier to digest on the client. + Any Presentation of type Presentations goes under 'maps'. Any of type ResultPage goes under 'results'. This + is done because Sproutcore can't easily handle having multiple classes in a single list. But really, it's + better for an API consumer to see them separated anyway. + :param bundle: + :return: + """ + return map_to_keyed_collections(lambda presentation: self.map_instance_to_class_key(presentation.obj), bundle.data['presentations']) + + def hydrate_presentations(self, bundle): + """ + Does the reverse of dehydrate_presentations. If the user actually wanted to create new presentations via the + API they'd simply save a presentation pointing to the correct configEntity, so we could probably just + disregard this list on post/patch/put. + :param bundle: + :return: + """ + if bundle.data.get('id', 0) == 0: + # We can't handle presentations on new config_entities yet. + # One problem is that tastypie/django doesn't like presentations that are actually + # layer_libraries and result_libraries that have layers and results, respectively + bundle.data['presentations'] = None + return bundle + + if bundle.data.get('presentations', None): + bundle.data['presentations'] = flatten(bundle.data['presentations'].values()) if isinstance(bundle.data['presentations'], dict) else bundle.data['presentations'] + else: + bundle.data['presentations'] = [] + return bundle + + +def add_categories(bundle, *submitted_categories): + """ + When the user updates the values of one or more categories, we assume that they want to delete the current Category instances with the same keys and replace them with the selected Category value. For instance, if a scenario has the Category key:'category' value:'smart' and the user chooses 'dumb' for the new value, we want to delete the Category instance valued by 'smart' and insert the one valued by 'dumb'. But we don't want to mess with Category instances that have different keys + """ + logger = logging.getLogger(__name__) + try: + submitted_categories_by_key = map_to_keyed_collections(lambda category: category.key, submitted_categories) + existing_categories_by_key = map_to_keyed_collections(lambda category: category.key, bundle.obj.categories.all()) + categories_to_add_or_maintain = flat_map_values(lambda key, categories: unique(categories, lambda category: category.value), + merge(existing_categories_by_key, submitted_categories_by_key)) + bundle.obj.categories.clear() + bundle.obj.categories.add(*categories_to_add_or_maintain) + except Exception, e: + logger.critical(e.message) + + +class CategoryResourceMixin(ModelResource): + # Allow this to be null since categories are currently copied on the server when cloning. + # They could easily be done on the client + categories = ToManyCustomAddField(CategoryResource, 'categories', null=True, full=True, add=add_categories) + + +class TagResourceMixin(ModelResource): + # TODO setting this read only because its causing permission problems on update!!! + tags = ToManyField(TagResource, 'tags', full=True, null=True, readonly=True) + + diff --git a/footprint/main/resources/model_dict_field.py b/footprint/main/resources/model_dict_field.py new file mode 100644 index 000000000..2345078f1 --- /dev/null +++ b/footprint/main/resources/model_dict_field.py @@ -0,0 +1,123 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import logging +from tastypie.fields import DictField +from footprint.main.lib.functions import deep_copy_dict_structure +from footprint.main.resources.footprint_resource import FootprintResource + +__author__ = 'calthorpe_associates' + +logger = logging.getLogger(__name__) + +class ModelDictField(DictField): + + def resolve_resource_field(self, *keys): + return self._resource.base_fields.get(keys[0], self.resolve_resource_field(*keys[1:]) if len(keys)>1 else None) + + def key_dehydrate_override(self): + """ + Override this method to create mapping from a model class dict key to different dehydrated object key. This is used for instance to map 'db_entities' to 'db_entity_interests' because the ConfigEntity resource class wants to present selects as DbEntityInterests + :return: + """ + return {} + + def key_hydrate_override(self): + """ + Override this method to create mapping from a model class dict key to different dehydrated object key. This is used for instance to map 'db_entities' to 'db_entity_interests' because the ConfigEntity resource class wants to present selects as DbEntityInterests + :return: + """ + return {} + + def instance_dehydrate_override(self): + """ + Overrides instances of the model class dict. Specify functions by dict key that map the instance to a new value. The function receives the bundle.obj and the object of the key. For instance {'db_entities':lambda config_entity, db_entity: db_entity_interest_of_db_entity(db_entity) } + :return: + """ + return {} + + def instance_hydrate_override(self): + """ + Overrides instances of the model class dict. Specify functions by dict key that map the instance to a new value. The function receives the bundle.obj and the object of the key. For instance {'db_entities':lambda config_entity, db_entity: db_entity_interest_of_db_entity(db_entity) } + :return: + """ + return {} + + def dehydrate(self, bundle): + """ + Handles the selections dict. This could be generalized into a custom field that handles a dictionary of assorted model instances and converts each one to a resource URI + :param bundle: + :return: + """ + + # Deep copy the structure to create new dict instance so we don't mutilate the source + value = deep_copy_dict_structure(super(ModelDictField, self).dehydrate(bundle)) + + return self.process_dict(value, bundle) + + def process_dict(self, dct, bundle, outer_key=None): + for key, value in (dct or {}).items(): + updated_key = self.key_dehydrate_override().get(key, key) + if updated_key != key: + del dct[key] + if isinstance(value, dict): + dct[updated_key] = value + if isinstance(value, dict): + self.process_dict(value, bundle, outer_key=updated_key) + else: + # value is a model instance that is to be dehydrated to a resource_uri + updated_model_instance = self.instance_dehydrate_override().get(updated_key, lambda x,y: value)(bundle.obj, value) + # Find the resource field on the resource that matches one of these keys + field = self.resolve_resource_field(*([outer_key, updated_key] if outer_key else [updated_key])) + if field: + field_resource = field.to_class() + elif value: + # Just search FootprintResource for a resource class that matches + field_resource = FootprintResource.match_existing_resources(value.__class__)[0]() + dct[updated_key] = field_resource.dehydrate_resource_uri(updated_model_instance) if value else None + return dct + + def hydrate(self, bundle): + """ + Hydrates a dict of resource URI to the corresponding instances by resolving the URIs. Like dehydrate_selections, this could be generalized + :param bundle: + :return: + """ + value = super(ModelDictField, self).hydrate(bundle) + + # Fill the dehydrated bundle for each outer key + for outer_key, dct in (value or {}).items(): + updated_outer_key = self.key_hydrate_override().get(outer_key, outer_key) + if (updated_outer_key != outer_key): + del value[outer_key] + value[updated_outer_key] = {} + # Each inner key value is a resource uri that is to be hydrated to an instance + for key, resource_uri in (dct or {}).items(): + updated_key = self.key_hydrate_override().get(key, key) + field = self.resolve_resource_field(outer_key, key) + if not field: + # Can't update the value. Probably corrupt data + logger.warn('No resource field matching either key %s or %s' % (outer_key, key)) + continue + field_resource = field.to_class() + if (updated_key != key): + del value[key] + value[updated_key] = {} + model_instance = field_resource.get_via_uri(resource_uri, bundle.request) + updated_instance = self.instance_hydrate_override().get(updated_outer_key, lambda x,y: model_instance)(bundle.obj, model_instance) + value[updated_outer_key][updated_key] = updated_instance + return value diff --git a/footprint/main/resources/pickled_dict_field.py b/footprint/main/resources/pickled_dict_field.py new file mode 100644 index 000000000..20d0adbb9 --- /dev/null +++ b/footprint/main/resources/pickled_dict_field.py @@ -0,0 +1,79 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import string +from tastypie.fields import DictField, NOT_PROVIDED, ApiField +from footprint.main.lib.functions import map_dict_to_dict, deep_copy_dict_structure, deep_copy + +__author__ = 'calthorpe_associates' + +class ObjectField(ApiField): + """ + Handles any object by turning it into a dict by recursively using each object's __dict__ attribute + Arrays are left as arrays + Since class data is removed a reference instance would be needed to rehydrate it + """ + dehydrated_type = 'dict' + help_text = "A dictionary of data. Ex: {'price': 26.73, 'name': 'Daniel'}" + + def convert(self, value): + if value is None: + return None + + return deep_copy(value, True) + +class PickledObjField(ObjectField): + """ + For read-only configurations, dehydration of arbitrary object graphs. Hydration isn't possible without having a reference instance to know the classes + """ + + def dehydrate(self, bundle): + """ + Handles the object dehydration + :param bundle: + :return: + """ + + # Deep copy the structure to create new dict instance so we don't mutilate the source + obj = super(PickledObjField, self).dehydrate(bundle) + return deep_copy(obj, True) + + +class PickledDictField(ApiField): + + def dehydrate(self, bundle): + """ + :param bundle: + :return: + """ + + # Deep copy the structure to create new dict instance so we don't mutilate the source + try: + return deep_copy(super(PickledDictField, self).dehydrate(bundle)) + except: + setattr(bundle.obj, self.attribute, None) # value got deformed--clear it + return deep_copy(super(PickledDictField, self).dehydrate(bundle)) + + def hydrate(self, bundle): + """ + Hydrates a dict of resource URI to the corresponding instances by resolving the URIs. Like dehydrate_selections, this could be generalized + :param bundle: + :return: + """ + value = super(PickledDictField, self).hydrate(bundle) + return value + diff --git a/footprint/main/resources/policy_resources.py b/footprint/main/resources/policy_resources.py new file mode 100644 index 000000000..44ae1f96b --- /dev/null +++ b/footprint/main/resources/policy_resources.py @@ -0,0 +1,41 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie.fields import ToManyField +from tastypie.resources import ModelResource +from footprint.main.models import PolicySet +from footprint.main.models.config.policy import Policy +from footprint.main.resources.footprint_resource import FootprintResource +from footprint.main.resources.tag_resource import TagResource + +__author__ = 'calthorpe_associates' + +class PolicyResource(FootprintResource): + policies = ToManyField('self', 'policies', full=True) + tags = ToManyField(TagResource, 'tags', full=True) + class Meta: + always_return_data = True + queryset = Policy.objects.all() + +class PolicySetResource(ModelResource): + policies = ToManyField(PolicyResource, 'policies', full=True) + + class Meta: + always_return_data = True + queryset = PolicySet.objects.all() + resource_name = 'policy_set' + diff --git a/footprint/main/resources/presentation_medium_resource.py b/footprint/main/resources/presentation_medium_resource.py new file mode 100644 index 000000000..491a5ecda --- /dev/null +++ b/footprint/main/resources/presentation_medium_resource.py @@ -0,0 +1,66 @@ +from tastypie import fields +from tastypie.fields import ListField, CharField +from footprint.main.lib.functions import remove_keys +from footprint.main.models import PresentationMedium +from footprint.main.resources.db_entity_resources import DbEntityInterestResource +from footprint.main.resources.footprint_resource import FootprintResource +from footprint.main.resources.medium_resources import MediumResource +from footprint.main.resources.mixins.mixins import TagResourceMixin +from footprint.main.resources.pickled_dict_field import PickledDictField +from footprint.main.resources.presentation_resources import PresentationResource + +__author__ = 'calthorpe' + +class PresentationMediumResource(FootprintResource, TagResourceMixin): + """ + The through class between Presentation and Medium, a list of which are loaded by a PresentationResource instance to give the user access to the corresponding Medium and also the important db_entity method, which returns the selected DbEntity interest of the PresentationMedium's db_entity_key + """ + + # The DbEntityInterest of the layer. + # This must be created when the layer is created + # DbEntityInterest is actually accessed through a getter/setter on the presentation_medium + # Since it belongs to the presentation.config_entity. + db_entity_interest = fields.ToOneField(DbEntityInterestResource, attribute='db_entity_interest', full=True, null=False) + + # The db_entity_key set by the db_entity_interest--update/create goes through db_entity_interest + db_entity_key = fields.CharField(attribute='db_entity_key', null=False) + def hydrate_db_entity_key(self, bundle): + if bundle.data['db_entity_interest']: + bundle.data['db_entity_key'] = bundle.data['db_entity_interest']['db_entity']['key'] + return bundle + + # Just return the uri to the Presentation, assuming we loaded this resource instance in the context of a Presentation + presentation = fields.ToOneField(PresentationResource, attribute='presentation', null=False, full=False) + + # Return the full Medium + medium = fields.ToOneField(MediumResource, attribute='medium', null=False, full=True) + # The currently configured context dict for the medium. These are generally the values edited by the user in the UI + medium_context = PickledDictField(attribute='medium_context', null=True, blank=True, default=lambda: {}) + # The configuration of items not directly related to the Medium, such as graph labels. These are usually also + # editable by the user + configuration = PickledDictField(attribute='configuration', null=True, blank=True, default=lambda: {}) + + visible_attributes = ListField(attribute='visible_attributes', null=True, blank=True) + + # The rendered version of the Medium that is rendered based on the current medium_context values and the + # configuration values + # This might be CSS, a dict of multiple CSS, or anything else that needs to be rendered on the server but + # shown in the browser + rendered_medium = CharField(attribute='rendered_medium', null=True, blank=True) + + def hydrate(self, bundle): + return super(PresentationMediumResource, self).hydrate(bundle) + + def dehydrate(self, bundle): + return super(PresentationMediumResource, self).dehydrate(bundle) + + def dehydrate_medium_context(self, bundle): + # Remove data that isn't needed by the API + return remove_keys(bundle.data['medium_context'], ['attributes']) + + class Meta(FootprintResource.Meta): + resource_name = 'presentation_medium' + always_return_data = True + queryset = PresentationMedium.objects.all() + excludes = ['rendered_medium'] + diff --git a/footprint/main/resources/presentation_resources.py b/footprint/main/resources/presentation_resources.py new file mode 100644 index 000000000..08f04c382 --- /dev/null +++ b/footprint/main/resources/presentation_resources.py @@ -0,0 +1,66 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from tastypie import fields +from tastypie.fields import CharField, ListField +from footprint.main.lib.functions import remove_keys +from footprint.main.models import PresentationConfiguration, PresentationMedium +from footprint.main.models.presentation.presentation import Presentation +from footprint.main.resources.config_entity_resources import ConfigEntityResource +from footprint.main.resources.db_entity_resources import DbEntityInterestResource +from footprint.main.resources.medium_resources import MediumResource +from footprint.main.resources.mixins.mixins import ToManyFieldWithSubclasses, TagResourceMixin +from footprint.main.resources.pickled_dict_field import PickledDictField, PickledObjField +from footprint.main.resources.footprint_resource import FootprintResource + +__author__ = 'calthorpe_associates' + +class PresentationResource(FootprintResource): + + # Returns instances of the through class, PresentationMedia or subclasses thereof + presentation_media_query = lambda bundle: bundle.obj.presentationmedium_set.all().select_subclasses() + presentation_media = ToManyFieldWithSubclasses( + 'footprint.main.resources.presentation_medium_resource.PresentationMediumResource', + attribute=presentation_media_query, + full=False, + null=True) + + # Just return the URI of thie config_entity, since it should always already be loaded by the user beforehand + config_entity = fields.ToOneField(ConfigEntityResource, 'config_entity', full=False) + + # Only turn on for debugging. This represents the initial configuration of the PresentationMedia, such as visibility + # configuration = fields.ToOneField('calthorpe.main.resources.presentation_resources.PresentationConfigurationResource', 'configuration', full=True) + + class Meta(FootprintResource.Meta): + always_return_data = True + queryset = Presentation.objects.all() + + + +class PresentationConfigurationResource(FootprintResource): + """ + These are not serialized as part of the API since they represent initial state and all important attributes are copied to the PresentationMedium instances. They can be turned on in PresentationResource for debugging purposes + """ + + data = PickledObjField(attribute='data', readonly=True, null=False, blank=False) + + class Meta(FootprintResource.Meta): + resource_name = 'presentation_configuration' + always_return_data = True + queryset = PresentationConfiguration.objects.all() + diff --git a/footprint/main/resources/resource_utils.py b/footprint/main/resources/resource_utils.py new file mode 100644 index 000000000..f8dc03c31 --- /dev/null +++ b/footprint/main/resources/resource_utils.py @@ -0,0 +1,32 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie.bundle import Bundle +from footprint.main.lib.functions import map_dict_to_dict, is_list_or_tuple + +__author__ = 'calthorpe_associates' + +def unbundle(bundle): + if isinstance(bundle, Bundle): + return map_dict_to_dict(lambda attr, value: [attr, unbundle(value)], bundle.data) + elif is_list_or_tuple(bundle): + return map(lambda value: unbundle(value), bundle) + else: + return bundle + +def unbundle_list(values): + map(lambda value: unbundle(value) if isinstance(value, Bundle) else value, values) diff --git a/footprint/main/resources/result_resources.py b/footprint/main/resources/result_resources.py new file mode 100644 index 000000000..ab6590d49 --- /dev/null +++ b/footprint/main/resources/result_resources.py @@ -0,0 +1,41 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie.fields import DictField +from footprint.main.resources.presentation_medium_resource import PresentationMediumResource +from footprint.main.resources.presentation_resources import PresentationResource +from footprint.main.models import ResultLibrary, Result +from footprint.main.resources.mixins.mixins import ToManyFieldWithSubclasses + +class ResultLibraryResource(PresentationResource): + + results = ToManyFieldWithSubclasses( + 'footprint.main.resources.result_resources.ResultResource', + attribute='results', + full=False, + null=True) + + class Meta(PresentationResource.Meta): + resource_name = 'result_library' + always_return_data = True + queryset = ResultLibrary.objects.all() + excludes = ['presentation_media'] + +class ResultResource(PresentationMediumResource): + + # Returns the results of the DbEntity query + query = DictField(attribute='query', null=False) + + class Meta(PresentationMediumResource.Meta): + resource_name = 'result' + always_return_data = True + queryset = Result.objects.all() + diff --git a/footprint/main/resources/tag_resource.py b/footprint/main/resources/tag_resource.py new file mode 100644 index 000000000..0c0524464 --- /dev/null +++ b/footprint/main/resources/tag_resource.py @@ -0,0 +1,29 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from tastypie.resources import ModelResource +from footprint.main.models.tag import Tag + +__author__ = 'calthorpe_associates' + +class TagResource(ModelResource): + """ + When tags are returned as ToMany properties of other classes, like DbEntity, they are simplified to an array of strings. I'm not sure what format they should be for individual requests. Maybe only lists should be supported + """ + class Meta: + always_return_data = True + queryset = Tag.objects.all() diff --git a/footprint/main/resources/user_resource.py b/footprint/main/resources/user_resource.py new file mode 100644 index 000000000..d0e615f6b --- /dev/null +++ b/footprint/main/resources/user_resource.py @@ -0,0 +1,128 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.auth import authenticate +from tastypie import fields +from tastypie.exceptions import NotFound + +__author__ = 'calthorpe_associates' + +from tastypie.models import create_api_key, ApiKey +from django.db import models +from django.contrib.auth.models import User +from tastypie.authentication import ApiKeyAuthentication, BasicAuthentication, Authentication +from tastypie.authorization import DjangoAuthorization +from tastypie.resources import ModelResource + +class ApiKeyResource(ModelResource): + class Meta: + always_return_data = True + queryset = ApiKey.objects.all() + resource_name = 'api_key' + authentication = ApiKeyAuthentication() + authorization = DjangoAuthorization() + +class UserAuthentication(Authentication): + def is_authenticated(self, request, **kwargs): + params = request.GET + if params.get('username', None) and params.get('password', None): + username = params['username'] + password = params['password'] + return authenticate(username=username, password=password) is not None + elif params.get('api_key', None): + return len(User.objects.filter(api_key__key=params['api_key'])) == 1 + + # Optional but recommended + def get_identifier(self, request): + return request.GET.get('username', None) + +class UserResource(ModelResource): + + # Include the ApiKey so that the user can make authenticated calls + api_key = fields.ToOneField(ApiKeyResource, 'api_key', full=True) + + def dehydrate_api_key(self, bundle): + """ + Expose only the ApiKey.key via the API. + :param bundle: + :return: + """ + return bundle.data['api_key'].data['key'] + + def hydrate_api_key(self, bundle): + """ + Convert the api key into a full instance if it matches + :param bundle: + :return: + """ + try: + bundle.obj.api_key = ApiKey.objects.filter(key=bundle.data['api_key']) + except NotFound: + bundle.obj.api_key = None + return bundle + + def build_filters(self, filters=None): + if filters is None: + filters = {} + + orm_filters = super(UserResource, self).build_filters(filters) + + if orm_filters.get('password__exact', None): + orm_filters.pop('password__exact') + if "api_key__exact" in orm_filters: + if orm_filters['api_key__exact']: + orm_filters['api_key__key__exact'] = orm_filters['api_key__exact'] + orm_filters.pop('api_key__exact') + + return orm_filters + + class Meta: + filtering = { + "id": ('exact',), + "username": ('exact',), + "password": ('exact',), + "api_key": ('exact',), + } + always_return_data = True + queryset = User.objects.all() + resource_name = 'user' + excludes = ['is_superuser', 'is_staff', 'last_login', 'date_joined'] + authentication = UserAuthentication() + authorization = DjangoAuthorization() + +models.signals.post_save.connect(create_api_key, sender=User) + +class ApiTokenResource(ModelResource): + class Meta(object): + queryset = ApiKey.objects.all() + resource_name = "token" + include_resource_uri = False + fields = ["key"] + list_allowed_methods = [] + detail_allowed_methods = ["get"] + authentication = BasicAuthentication() + + def obj_get(self, request=None, **kwargs): + if kwargs["pk"] != "auth": + raise NotImplementedError("Resource not found") + + user = request.user + if not user.is_active: + raise NotFound("User not active") + + api_key = ApiKey.objects.get(user=request.user) + return api_key diff --git a/footprint/main/samples/Sutter_Cities.zip b/footprint/main/samples/Sutter_Cities.zip new file mode 100644 index 000000000..c7287157d Binary files /dev/null and b/footprint/main/samples/Sutter_Cities.zip differ diff --git a/footprint/main/samples/TAZ07_w_tahoe.zip b/footprint/main/samples/TAZ07_w_tahoe.zip new file mode 100644 index 000000000..3bdb53a50 Binary files /dev/null and b/footprint/main/samples/TAZ07_w_tahoe.zip differ diff --git a/footprint/main/samples/ZipCodes2009.zip b/footprint/main/samples/ZipCodes2009.zip new file mode 100755 index 000000000..09b3aa699 Binary files /dev/null and b/footprint/main/samples/ZipCodes2009.zip differ diff --git a/footprint/main/templates/admin/change_form.html b/footprint/main/templates/admin/change_form.html new file mode 100644 index 000000000..e33227594 --- /dev/null +++ b/footprint/main/templates/admin/change_form.html @@ -0,0 +1,70 @@ +{% extends "../admin/change_form.html" %} + +{% load wysiwyg %} + +{% block extrahead %} + + {{ block.super }} + + {% wysiwyg_setup %} + +{% endblock %} + +{% block extrastyle %} + + {{ block.super }} + + + +{% endblock %} + +{% block content %} + + {{ block.super }} + + {# Replace XXXXXXXX with field name: #} + {% wysiwyg_editor "id_description" %} + {# ... add more here... #} + +{% endblock %} \ No newline at end of file diff --git a/footprint/main/templates/footprint/chart_sandbox.html b/footprint/main/templates/footprint/chart_sandbox.html new file mode 100644 index 000000000..c92892651 --- /dev/null +++ b/footprint/main/templates/footprint/chart_sandbox.html @@ -0,0 +1,29 @@ + + + + chart sandbox + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +
    + + + \ No newline at end of file diff --git a/footprint/main/templates/footprint/chart_sandbox2.html b/footprint/main/templates/footprint/chart_sandbox2.html new file mode 100644 index 000000000..e420ef03b --- /dev/null +++ b/footprint/main/templates/footprint/chart_sandbox2.html @@ -0,0 +1,56 @@ + + + + chart sandbox + + + + + +
    +
    + + + diff --git a/footprint/main/templates/footprint/charts.html b/footprint/main/templates/footprint/charts.html new file mode 100644 index 000000000..c55f45ec5 --- /dev/null +++ b/footprint/main/templates/footprint/charts.html @@ -0,0 +1,586 @@ + + + + + Stacked and Grouped Bar with Animation + + + + +

    Stacked to Grouped Bar Chart

    +
    +
    +
    +

    Legend


    +
    + + + +
    + +
    + diff --git a/footprint/main/templates/footprint/d3map.html b/footprint/main/templates/footprint/d3map.html new file mode 100644 index 000000000..21d718e29 --- /dev/null +++ b/footprint/main/templates/footprint/d3map.html @@ -0,0 +1,114 @@ + + + + + + + + + diff --git a/footprint/main/templates/footprint/error.html b/footprint/main/templates/footprint/error.html new file mode 100644 index 000000000..c33ee5f19 --- /dev/null +++ b/footprint/main/templates/footprint/error.html @@ -0,0 +1,27 @@ + + +{% extends "footprint/footprint_base.html" %} + +{% block maincontent %} + +
    + icon +

    Oops !

    +

    {{msg}}

    + +
    + + +{% endblock maincontent%} \ No newline at end of file diff --git a/footprint/main/templates/footprint/google_map.html b/footprint/main/templates/footprint/google_map.html new file mode 100644 index 000000000..0314a30bb --- /dev/null +++ b/footprint/main/templates/footprint/google_map.html @@ -0,0 +1,28 @@ + + + + + + + + + +
    + + \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/BuiltForm__id.cartocss b/footprint/main/templates/footprint/maps/BuiltForm__id.cartocss new file mode 100644 index 000000000..549b1825b --- /dev/null +++ b/footprint/main/templates/footprint/maps/BuiltForm__id.cartocss @@ -0,0 +1,19 @@ +{line-color: #FFF;} + +[zoom<=13] {line-width:0;} +[zoom=14] {line-width:0.2;} +[zoom=15] {line-width:0.7;} +[zoom=16] {line-width:1;} +[zoom=17] {line-width:1.3;} +[zoom=18] {line-width:2.4;} +[zoom=19] {line-width:2.4;} +[zoom>=20] {line-width:3;} + +{% for key, value in attributes.built_form__id.equals.items %} + +[built_form__id={{ key }}] { +{% if value.fill.color %} polygon-fill: {{ value.fill.color }}; {% endif %} +{% if value.outline.color %} line-color: {{ value.outline.color }}; {% endif %} +polygon-opacity: {% if value.fill.opacity %}{{ value.fill.opacity }}{% else %}.6{% endif %} +} +{% endfor %} diff --git a/footprint/main/templates/footprint/maps/BuiltForm__id.css b/footprint/main/templates/footprint/maps/BuiltForm__id.css new file mode 100644 index 000000000..7e1b954e0 --- /dev/null +++ b/footprint/main/templates/footprint/maps/BuiltForm__id.css @@ -0,0 +1,6 @@ +{% for key, value in builtform__id.equals.items %}.builtform_id-{{ key }} { + {% if value.fill.color %} fill: {{ value.fill.color }}; {% endif %} + {% if value.fill.opacity %} opacity: {{ value.fill.opacity }}; {% endif %} + stroke: {% if value.outline.color %} {{ value.outline.color }} + {% else %} #FFFFFF {% endif %}; +} {% endfor %} diff --git a/footprint/main/templates/footprint/maps/CensusBlock__block.cartocss b/footprint/main/templates/footprint/maps/CensusBlock__block.cartocss new file mode 100644 index 000000000..e0efd2497 --- /dev/null +++ b/footprint/main/templates/footprint/maps/CensusBlock__block.cartocss @@ -0,0 +1,22 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<14] { + outline/line-width: 2; + outline/line-color:#000000; + outline/line-opacity:0.7; + mainline/line-width:1; + mainline/line-color:#CCFFFF; + mainline/line-opacity:1; +} + +[zoom>=15] { + outline/line-width: 5; + outline/line-color:#000000; + outline/line-opacity:0.7; + mainline/line-width:1.6; + mainline/line-color:#CCFFFF; + mainline/line-opacity:1; + mainline/line-dasharray:3,1; +} + diff --git a/footprint/main/templates/footprint/maps/CensusBlock__block.css b/footprint/main/templates/footprint/maps/CensusBlock__block.css new file mode 100644 index 000000000..01ca9d297 --- /dev/null +++ b/footprint/main/templates/footprint/maps/CensusBlock__block.css @@ -0,0 +1,6 @@ +.selected { + fill: #FFFFFF; + opacity: 0.2; + stroke: #FFFFFF; +} + diff --git a/footprint/main/templates/footprint/maps/CensusBlockgroup__blockgroup.cartocss b/footprint/main/templates/footprint/maps/CensusBlockgroup__blockgroup.cartocss new file mode 100644 index 000000000..2fb962fbe --- /dev/null +++ b/footprint/main/templates/footprint/maps/CensusBlockgroup__blockgroup.cartocss @@ -0,0 +1,63 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom=11] { + outline/line-width: 1.2; + outline/line-color:#000000; + outline/line-opacity:0.8; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 0.5; + mainline/line-color:#ffaa00; + mainline/line-opacity:1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=12] { + outline/line-width: 2.2; + outline/line-color:#000000; + outline/line-opacity:0.8; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 0.7; + mainline/line-color:#ffaa00; + mainline/line-opacity:1; + mainline/line-join: round; + mainline/line-cap: round;} + +[zoom=13] { + outline/line-width: 4; + outline/line-color:#000000; + outline/line-opacity:0.8; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 1.4; + mainline/line-color:#ffaa00; + mainline/line-opacity:1; + mainline/line-join: round; + mainline/line-cap: round;} + +[zoom=14] { + outline/line-width: 5; + outline/line-color:#000000; + outline/line-opacity:0.8; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 2; + mainline/line-color:#ffaa00; + mainline/line-opacity:1; + mainline/line-join: round; + mainline/line-cap: round;} + +[zoom>=15] { + outline/line-width: 8; + outline/line-color:#000000; + outline/line-opacity:0.8; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 4; + mainline/line-color:#ffaa00; + mainline/line-opacity:1; + mainline/line-join: round; + mainline/line-cap: round;} diff --git a/footprint/main/templates/footprint/maps/CensusBlockgroup__blockgroup.css b/footprint/main/templates/footprint/maps/CensusBlockgroup__blockgroup.css new file mode 100644 index 000000000..6d58776a4 --- /dev/null +++ b/footprint/main/templates/footprint/maps/CensusBlockgroup__blockgroup.css @@ -0,0 +1,5 @@ +.selected { + fill: #FFFFFF; + opacity: 0.2; + stroke: #FFFFFF; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/CensusTract__tract.cartocss b/footprint/main/templates/footprint/maps/CensusTract__tract.cartocss new file mode 100644 index 000000000..983b4a678 --- /dev/null +++ b/footprint/main/templates/footprint/maps/CensusTract__tract.cartocss @@ -0,0 +1,65 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<12] { + outline/line-width: 2; + outline/line-color:#000000; + outline/line-opacity: 0.8; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 1.2; + mainline/line-color:#80FFFF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=12] { + outline/line-width: 3.8; + outline/line-color:#000000; + outline/line-opacity: 0.8; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 1.8; + mainline/line-color:#80FFFF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=13] { + outline/line-width: 6.5; + outline/line-color:#000000; + outline/line-opacity: 0.8; + mainline/line-width: 2.5; + mainline/line-color:#80FFFF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=14] { + outline/line-width: 7; + outline/line-color:#000000; + outline/line-opacity: 0.8; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 3; + mainline/line-color:#80FFFF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom>=15] { + outline/line-width: 9; + outline/line-color:#000000; + outline/line-opacity: 0.8; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 3.8; + mainline/line-color:#80FFFF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/CensusTract__tract.css b/footprint/main/templates/footprint/maps/CensusTract__tract.css new file mode 100644 index 000000000..460de72dc --- /dev/null +++ b/footprint/main/templates/footprint/maps/CensusTract__tract.css @@ -0,0 +1,6 @@ +.selected { + fill: #FFFFFF; + opacity: 0.2; + stroke: #FFFFFF; +} + diff --git a/footprint/main/templates/footprint/maps/CoreIncrementFeature.cartocss b/footprint/main/templates/footprint/maps/CoreIncrementFeature.cartocss new file mode 100644 index 000000000..01c5b7d16 --- /dev/null +++ b/footprint/main/templates/footprint/maps/CoreIncrementFeature.cartocss @@ -0,0 +1,20 @@ + + +[ land_development_category = 'urban'] { + polygon-fill:#CC00CC; + line-color: #909090; + line-width:1; +} + +[ land_development_category = 'compact'] { + polygon-fill:#33CCFF; + line-color: #909090; + line-width:1; +} + +[ land_development_category = 'standard'] { + polygon-fill:#FFCC66; + line-color: #909090; + line-width:1; +} + diff --git a/footprint/main/templates/footprint/maps/CoreIncrementFeature.css b/footprint/main/templates/footprint/maps/CoreIncrementFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/CpadHoldingsFeature.cartocss b/footprint/main/templates/footprint/maps/CpadHoldingsFeature.cartocss new file mode 100644 index 000000000..0898ba947 --- /dev/null +++ b/footprint/main/templates/footprint/maps/CpadHoldingsFeature.cartocss @@ -0,0 +1,16 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<=10] { + line-color:#594; + line-width:0.8; + polygon-opacity:1; + polygon-fill:#8DA24D; +} + +[zoom>10] { + line-color:#594; + line-width:0.8; + polygon-opacity:1; + polygon-fill:#8DA24D; +} diff --git a/footprint/main/templates/footprint/maps/CpadHoldingsFeature.css b/footprint/main/templates/footprint/maps/CpadHoldingsFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/DevelopableFeature.cartocss b/footprint/main/templates/footprint/maps/DevelopableFeature.cartocss new file mode 100644 index 000000000..6eb6511b0 --- /dev/null +++ b/footprint/main/templates/footprint/maps/DevelopableFeature.cartocss @@ -0,0 +1,32 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[ developable_index > 0.8] { + polygon-fill:#009966; + line-color: #909090; + line-width:1; +} + +[ developable_index <= 0.8] { + polygon-fill:#00FF66; + line-color: #909090; + line-width:1; +} + +[ developable_index <= 0.6] { + polygon-fill:#FFFF00; + line-color: #909090; + line-width:1; +} + +[ developable_index <= 0.4] { + polygon-fill:#FF9933; + line-color: #909090; + line-width:1; +} + +[ developable_index <= 0.2] { + polygon-fill:#CC0033; + line-color: #909090; + line-width:1; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/DevelopableFeature.css b/footprint/main/templates/footprint/maps/DevelopableFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/Feature.cartocss b/footprint/main/templates/footprint/maps/Feature.cartocss new file mode 100644 index 000000000..5c59b4888 --- /dev/null +++ b/footprint/main/templates/footprint/maps/Feature.cartocss @@ -0,0 +1,21 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<15] { + outline/line-width: 2; + outline/line-color:#000000; + outline/line-opacity:0.7; + mainline/line-width:1; + mainline/line-color:#88FF88; + mainline/line-opacity:1; +} + +[zoom>=15] { + outline/line-width: 5; + outline/line-color:#000000; + outline/line-opacity:0.7; + mainline/line-width:1.6; + mainline/line-color:#88FF88; + mainline/line-opacity:1; + mainline/line-dasharray:3,1; +} diff --git a/footprint/main/templates/footprint/maps/Feature.css b/footprint/main/templates/footprint/maps/Feature.css new file mode 100644 index 000000000..31634a383 --- /dev/null +++ b/footprint/main/templates/footprint/maps/Feature.css @@ -0,0 +1,7 @@ +.selected { + {% if selected.equals.true.fill.color %} fill: {{ selected.equals.true.fill.color }}; {% endif %} + {% if selected.equals.true.fill.opacity %} opacity: {{ value.fill.opacity }}; {% endif %} + {% if selected.equals.true.outline_color %} stroke: {{ selected.equals.true.outline_color }}; {% endif %} + {# TODO: allow options for line styling here #} +} + diff --git a/footprint/main/templates/footprint/maps/Feature__built_form__id.cartocss b/footprint/main/templates/footprint/maps/Feature__built_form__id.cartocss new file mode 100644 index 000000000..71dbaced1 --- /dev/null +++ b/footprint/main/templates/footprint/maps/Feature__built_form__id.cartocss @@ -0,0 +1,23 @@ + +[zoom<=13] {line-width:0;} +[zoom=14] {line-width:0.1;} +[zoom=15] {line-width:0.7;} +[zoom=16] {line-width:1;} +[zoom=17] {line-width:1.3;} +[zoom=18] {line-width:2.4;} +[zoom=19] {line-width:2.4;} +[zoom>=20] {line-width:3;} + + + +{% for key, value in attributes.built_form__id.equals.items %} + +[built_form__id={{ key }}] { +{% if value.fill.color %} polygon-fill: {{ value.fill.color }}; {% endif %} + polygon-opacity: .6; +} +{% endfor %} + +[built_form__id=null] { + polygon-opacity: .1; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/Feature__built_form__id.css b/footprint/main/templates/footprint/maps/Feature__built_form__id.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/LayerSelection__selected.cartocss b/footprint/main/templates/footprint/maps/LayerSelection__selected.cartocss new file mode 100644 index 000000000..0898ba947 --- /dev/null +++ b/footprint/main/templates/footprint/maps/LayerSelection__selected.cartocss @@ -0,0 +1,16 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<=10] { + line-color:#594; + line-width:0.8; + polygon-opacity:1; + polygon-fill:#8DA24D; +} + +[zoom>10] { + line-color:#594; + line-width:0.8; + polygon-opacity:1; + polygon-fill:#8DA24D; +} diff --git a/footprint/main/templates/footprint/maps/LayerSelection__selected.css b/footprint/main/templates/footprint/maps/LayerSelection__selected.css new file mode 100644 index 000000000..3256c0b63 --- /dev/null +++ b/footprint/main/templates/footprint/maps/LayerSelection__selected.css @@ -0,0 +1,6 @@ +.selected { + {% if selected.equals.true.fill.color %} fill: {{ selected.equals.true.fill.color }}; {% endif %} + {% if selected.equals.true.fill.opacity %} opacity: {{ value.fill.opacity }}; {% endif %} + {% if selected.equals.true.outline_color %} stroke: {{ selected.equals.true.outline_color }}; {% endif %} + {# TODO: allow options for line styling here #} +} diff --git a/footprint/main/templates/footprint/maps/PrimaryParcelFeature__land_use_definitions__id.cartocss b/footprint/main/templates/footprint/maps/PrimaryParcelFeature__land_use_definitions__id.cartocss new file mode 100644 index 000000000..cfeb22929 --- /dev/null +++ b/footprint/main/templates/footprint/maps/PrimaryParcelFeature__land_use_definitions__id.cartocss @@ -0,0 +1,15 @@ + +[zoom<=13] {line-width:0;} +[zoom=14] {line-width:0.2;} +[zoom=15] {line-width:0.7;} +[zoom=16] {line-width:1;} +[zoom=17] {line-width:1.3;} +[zoom=18] {line-width:2.4;} +[zoom=19] {line-width:2.4;} +[zoom>=20] {line-width:3;} + +{% for key, value in attributes.land_use_definition__id.equals.items %} +[land_use_definition__id={{ key }}] { +{% if value.fill.color %} polygon-fill: {{ value.fill.color }}; {% endif %} +} +{% endfor %} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/PrimaryParcelFeature__land_use_definitions__id.css b/footprint/main/templates/footprint/maps/PrimaryParcelFeature__land_use_definitions__id.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/SacogExistingLandUseParcelFeature__land_use_definition__id.cartocss b/footprint/main/templates/footprint/maps/SacogExistingLandUseParcelFeature__land_use_definition__id.cartocss new file mode 100644 index 000000000..cffd0c6fd --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogExistingLandUseParcelFeature__land_use_definition__id.cartocss @@ -0,0 +1,16 @@ + +[zoom<=13] {line-width:0;} +[zoom=14] {line-width:0.2;} +[zoom=15] {line-width:0.7;} +[zoom=16] {line-width:1;} +[zoom=17] {line-width:1.3;} +[zoom=18] {line-width:2.4;} +[zoom=19] {line-width:2.4;} +[zoom>=20] {line-width:3;} + +{% for key, value in attributes.land_use_definition__id.equals.items %} +[land_use_definition__id={{ key }}] { +{% if value.fill.color %} polygon-fill: {{ value.fill.color }}; {% endif %} +polygon-opacity: .6; +} +{% endfor %} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/SacogExistingLandUseParcelFeature__land_use_definition__id.css b/footprint/main/templates/footprint/maps/SacogExistingLandUseParcelFeature__land_use_definition__id.css new file mode 100644 index 000000000..90fd4a3e6 --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogExistingLandUseParcelFeature__land_use_definition__id.css @@ -0,0 +1,7 @@ +{% for key, value in land_use_definition__id.equals.items %}.lu-{{ key }} { + + {% if value.fill.color %} fill: {{ value.fill.color }}; {% endif %} + {% if value.fill.opacity %} opacity: {{ value.fill.opacity }}; {% endif %} + stroke: {% if value.outline.color %} {{ value.outline.color }}; + {% else %} #FFFFFF {% endif %} +} {% endfor %} diff --git a/footprint/main/templates/footprint/maps/SacogHardwoodFeature.cartocss b/footprint/main/templates/footprint/maps/SacogHardwoodFeature.cartocss new file mode 100644 index 000000000..5b9f50136 --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogHardwoodFeature.cartocss @@ -0,0 +1,17 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<=10] { + line-color:#007A5C; + line-width:0.5; + polygon-opacity:1; + polygon-fill:#66CCFF; +} + +[zoom>10] { + line-color:#007A5C; + line-width:1; + polygon-opacity:1; + polygon-fill:#66CCFF; +} + diff --git a/footprint/main/templates/footprint/maps/SacogHardwoodFeature.css b/footprint/main/templates/footprint/maps/SacogHardwoodFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/SacogLightRailFeature.cartocss b/footprint/main/templates/footprint/maps/SacogLightRailFeature.cartocss new file mode 100644 index 000000000..98c1b004b --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogLightRailFeature.cartocss @@ -0,0 +1,16 @@ + +[ line = "Gold"] { + line-color:#FFFF00; + line-width:4; +} + +[ line = "Blue"] { + line-color:#0000FF; + line-width:4; +} + +[ line = "Both"] { + line-color:#CC33FF; + line-width:4; +} + diff --git a/footprint/main/templates/footprint/maps/SacogLightRailFeature.css b/footprint/main/templates/footprint/maps/SacogLightRailFeature.css new file mode 100644 index 000000000..139597f9c --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogLightRailFeature.css @@ -0,0 +1,2 @@ + + diff --git a/footprint/main/templates/footprint/maps/SacogLightRailStopsFeature.cartocss b/footprint/main/templates/footprint/maps/SacogLightRailStopsFeature.cartocss new file mode 100644 index 000000000..b353345db --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogLightRailStopsFeature.cartocss @@ -0,0 +1,16 @@ + +[ color = 1] { + marker-width:10; + marker-fill:#FFFF00; + marker-line-color:white; + marker-line-width:1; +} + +[ color = 0] { + marker-width:10; + marker-fill:#0000FF; + marker-line-color:white; + marker-line-width:1; +} + + diff --git a/footprint/main/templates/footprint/maps/SacogLightRailStopsFeature.css b/footprint/main/templates/footprint/maps/SacogLightRailStopsFeature.css new file mode 100644 index 000000000..139597f9c --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogLightRailStopsFeature.css @@ -0,0 +1,2 @@ + + diff --git a/footprint/main/templates/footprint/maps/SacogLightRailStopsHalfMileFeature.cartocss b/footprint/main/templates/footprint/maps/SacogLightRailStopsHalfMileFeature.cartocss new file mode 100644 index 000000000..63cfdb5da --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogLightRailStopsHalfMileFeature.cartocss @@ -0,0 +1,57 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<12] { + outline/line-width: 1.6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 1; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.15; +} + +[zoom=12] { + outline/line-width: 3.8; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 1.8; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.1; +} + +[zoom=13] { + outline/line-width: 5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 2.6; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} + +[zoom=14] { + outline/line-width: 6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 2.8; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} + +[zoom>=15] { + outline/line-width: 7.5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 3.5; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/SacogLightRailStopsHalfMileFeature.css b/footprint/main/templates/footprint/maps/SacogLightRailStopsHalfMileFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/SacogLightRailStopsOneMileFeature.cartocss b/footprint/main/templates/footprint/maps/SacogLightRailStopsOneMileFeature.cartocss new file mode 100644 index 000000000..63cfdb5da --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogLightRailStopsOneMileFeature.cartocss @@ -0,0 +1,57 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<12] { + outline/line-width: 1.6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 1; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.15; +} + +[zoom=12] { + outline/line-width: 3.8; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 1.8; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.1; +} + +[zoom=13] { + outline/line-width: 5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 2.6; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} + +[zoom=14] { + outline/line-width: 6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 2.8; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} + +[zoom>=15] { + outline/line-width: 7.5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 3.5; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/SacogLightRailStopsOneMileFeature.css b/footprint/main/templates/footprint/maps/SacogLightRailStopsOneMileFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/SacogLightRailStopsQuarterMileFeature.cartocss b/footprint/main/templates/footprint/maps/SacogLightRailStopsQuarterMileFeature.cartocss new file mode 100644 index 000000000..63cfdb5da --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogLightRailStopsQuarterMileFeature.cartocss @@ -0,0 +1,57 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<12] { + outline/line-width: 1.6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 1; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.15; +} + +[zoom=12] { + outline/line-width: 3.8; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 1.8; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.1; +} + +[zoom=13] { + outline/line-width: 5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 2.6; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} + +[zoom=14] { + outline/line-width: 6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 2.8; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} + +[zoom>=15] { + outline/line-width: 7.5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 3.5; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/SacogLightRailStopsQuarterMileFeature.css b/footprint/main/templates/footprint/maps/SacogLightRailStopsQuarterMileFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/SacogStreamFeature.cartocss b/footprint/main/templates/footprint/maps/SacogStreamFeature.cartocss new file mode 100644 index 000000000..1a66439e7 --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogStreamFeature.cartocss @@ -0,0 +1,17 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<=10] { + line-color:#408080; + line-width:0.2; + polygon-opacity:0.95; + polygon-fill:#80FFFF; +} + +[zoom>10] { + line-color:#408080; + line-width:.2; + polygon-opacity:0.95; + polygon-fill:#80FFFF; +} + diff --git a/footprint/main/templates/footprint/maps/SacogStreamFeature.css b/footprint/main/templates/footprint/maps/SacogStreamFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/SacogVernalPoolFeature.cartocss b/footprint/main/templates/footprint/maps/SacogVernalPoolFeature.cartocss new file mode 100644 index 000000000..5b9f50136 --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogVernalPoolFeature.cartocss @@ -0,0 +1,17 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<=10] { + line-color:#007A5C; + line-width:0.5; + polygon-opacity:1; + polygon-fill:#66CCFF; +} + +[zoom>10] { + line-color:#007A5C; + line-width:1; + polygon-opacity:1; + polygon-fill:#66CCFF; +} + diff --git a/footprint/main/templates/footprint/maps/SacogVernalPoolFeature.css b/footprint/main/templates/footprint/maps/SacogVernalPoolFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/SacogWetlandFeature.cartocss b/footprint/main/templates/footprint/maps/SacogWetlandFeature.cartocss new file mode 100644 index 000000000..908dcac8b --- /dev/null +++ b/footprint/main/templates/footprint/maps/SacogWetlandFeature.cartocss @@ -0,0 +1,16 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom<=10] { + line-color:#007A5C; + line-width:0.5; + polygon-opacity:1; + polygon-fill:#33D6AD; +} + +[zoom>10] { + line-color:#007A5C; + line-width:1; + polygon-opacity:1; + polygon-fill:#33D6AD; +} diff --git a/footprint/main/templates/footprint/maps/SacogWetlandFeature.css b/footprint/main/templates/footprint/maps/SacogWetlandFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/Sandag2050RtpTransitNetworkFeature.cartocss b/footprint/main/templates/footprint/maps/Sandag2050RtpTransitNetworkFeature.cartocss new file mode 100644 index 000000000..8f7038ef7 --- /dev/null +++ b/footprint/main/templates/footprint/maps/Sandag2050RtpTransitNetworkFeature.cartocss @@ -0,0 +1,31 @@ + +[ transit_mode = 4] { + line-color:#404040; + line-width:4; +} + +[ transit_mode = 5] { + line-color: #FF0000; + line-width:4; +} + +[ transit_mode = 6] { + line-color:#FFFF00; + line-width:4; +} + +[ transit_mode = 7] { + line-color:#FF3366; + line-width:1; + line-opacity:0.3; +} + +[ transit_mode = 9] { + line-color:#FF3366; + line-width:1; +} + +[ transit_mode = 10] { + line-color:#6666FF; + line-width:1; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/Sandag2050RtpTransitNetworkFeature.css b/footprint/main/templates/footprint/maps/Sandag2050RtpTransitNetworkFeature.css new file mode 100644 index 000000000..139597f9c --- /dev/null +++ b/footprint/main/templates/footprint/maps/Sandag2050RtpTransitNetworkFeature.css @@ -0,0 +1,2 @@ + + diff --git a/footprint/main/templates/footprint/maps/Sandag2050RtpTransitStopFeature.cartocss b/footprint/main/templates/footprint/maps/Sandag2050RtpTransitStopFeature.cartocss new file mode 100644 index 000000000..aa559d882 --- /dev/null +++ b/footprint/main/templates/footprint/maps/Sandag2050RtpTransitStopFeature.cartocss @@ -0,0 +1,35 @@ + +[ transit_mode = 4] { + marker-width:10; + marker-fill:#404040; + marker-line-color:white; + marker-line-width:1; +} + +[ transit_mode = 5] { + marker-width:10; + marker-fill:#990033; + marker-line-color:white; + marker-line-width:1; +} + +[ transit_mode = 6] { + marker-width:10; + marker-fill:#CCFF00; + marker-line-color:black; + marker-line-width:1; +} + +[ transit_mode = 7] { + marker-width:6; + marker-fill:#FF6600; + marker-line-color:white; + marker-line-width:1; +} + +[ transit_mode = 8] { + marker-width:6; + marker-fill:#FF6600; + marker-line-color:white; + marker-line-width:1; +} diff --git a/footprint/main/templates/footprint/maps/Sandag2050RtpTransitStopFeature.css b/footprint/main/templates/footprint/maps/Sandag2050RtpTransitStopFeature.css new file mode 100644 index 000000000..139597f9c --- /dev/null +++ b/footprint/main/templates/footprint/maps/Sandag2050RtpTransitStopFeature.css @@ -0,0 +1,2 @@ + + diff --git a/footprint/main/templates/footprint/maps/SandagScenarioBBoundaryFeature.cartocss b/footprint/main/templates/footprint/maps/SandagScenarioBBoundaryFeature.cartocss new file mode 100644 index 000000000..4bc5b1f08 --- /dev/null +++ b/footprint/main/templates/footprint/maps/SandagScenarioBBoundaryFeature.cartocss @@ -0,0 +1,34 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +@citywidth: 2.5; +@citydash: 5; +@cityspace: 4; + +[zoom<=9] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 0.4; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#00CCFF; + mainline/line-width: @citywidth * 0.2; + mainline/line-dasharray: @citydash * 0.5, @cityspace * 0.5; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom>9] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 1; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#00CCFF; + mainline/line-width: @citywidth * 0.5; + mainline/line-dasharray: @citydash * 0.5, @cityspace * 0.5; + mainline/line-join: round; + mainline/line-cap: round; +} + + diff --git a/footprint/main/templates/footprint/maps/SandagScenarioBBoundaryFeature.css b/footprint/main/templates/footprint/maps/SandagScenarioBBoundaryFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/SandagScenarioCBoundaryFeature.cartocss b/footprint/main/templates/footprint/maps/SandagScenarioCBoundaryFeature.cartocss new file mode 100644 index 000000000..75e5fe8fd --- /dev/null +++ b/footprint/main/templates/footprint/maps/SandagScenarioCBoundaryFeature.cartocss @@ -0,0 +1,35 @@ + +.{{ htmlClass }} +{buffer-size: 50;} + +@citywidth: 2.5; +@citydash: 5; +@cityspace: 4; + +[zoom<=9] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 0.4; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#00FFFF; + mainline/line-width: @citywidth * 0.2; + mainline/line-dasharray: @citydash * 0.5, @cityspace * 0.5; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom>9] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 1; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#00FFFF; + mainline/line-width: @citywidth * 0.5; + mainline/line-dasharray: @citydash * 0.5, @cityspace * 0.5; + mainline/line-join: round; + mainline/line-cap: round; +} + + diff --git a/footprint/main/templates/footprint/maps/SandagScenarioCBoundaryFeature.css b/footprint/main/templates/footprint/maps/SandagScenarioCBoundaryFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/ScagExistingLandUseParcelFeature__land_use_definition_id.cartocss b/footprint/main/templates/footprint/maps/ScagExistingLandUseParcelFeature__land_use_definition_id.cartocss new file mode 100644 index 000000000..0a8ac973a --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagExistingLandUseParcelFeature__land_use_definition_id.cartocss @@ -0,0 +1,22 @@ + + +.{{ htmlClass }} +{line-color: #000000} + + +[zoom<=13] {line-width:0;} +[zoom=14] {line-width:0.2;} +[zoom=15] {line-width:0.7;} +[zoom=16] {line-width:1;} +[zoom=17] {line-width:1.3;} +[zoom=18] {line-width:2.4;} +[zoom=19] {line-width:2.4;} +[zoom>=20] {line-width:3;} + +{% for key, value in attributes.land_use_definition__id.equals.items %} +[land_use_definition__id={{ key }}] { +{% if value.fill.color %} polygon-fill: {{ value.fill.color }}; {% endif %} +{% if value.outline.color %}line-color: {{ value.outline.color }}; {% endif %} + +polygon-opacity: {% if value.fill.opacity %} {{ value.fill.opacity }}{% else %}.6{% endif %}; +} {% endfor %} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/ScagExistingLandUseParcelFeature__land_use_definition_id.css b/footprint/main/templates/footprint/maps/ScagExistingLandUseParcelFeature__land_use_definition_id.css new file mode 100644 index 000000000..be8cd1ea5 --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagExistingLandUseParcelFeature__land_use_definition_id.css @@ -0,0 +1,7 @@ +{% for key, value in land_use_definition.equals.items %}.lu-{{ key }} { + + {% if value.fill.color %} fill: {{ value.fill.color }}; {% endif %} + {% if value.fill.opacity %} opacity: {{ value.fill.opacity }}; {% endif %} + stroke: {% if value.outline.color %} {{ value.outline.color }}; + {% else %} #FFFFFF {% endif %} +} {% endfor %} diff --git a/footprint/main/templates/footprint/maps/ScagFloodplainFeature.cartocss b/footprint/main/templates/footprint/maps/ScagFloodplainFeature.cartocss new file mode 100644 index 000000000..6875ca54e --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagFloodplainFeature.cartocss @@ -0,0 +1,34 @@ +.{{ htmlClass }} +{buffer-size: 50;} + +[zoom=12] { + outline/line-width: 1; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + line-color:#3399FF; + line-width:1; +} + +[zoom=13] { + outline/line-width: 2; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + line-color:#3399FF; + line-width:1.5; +} + +[zoom=14] { + outline/line-width: 4; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + line-color:#3399FF; + line-width:2; +} + +[zoom>=15] { + outline/line-width: 6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + line-color:#3399FF; + line-width:3; +} diff --git a/footprint/main/templates/footprint/maps/ScagFloodplainFeature.css b/footprint/main/templates/footprint/maps/ScagFloodplainFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/ScagGeneralPlanParcelFeature__land_use_definition_id.cartocss b/footprint/main/templates/footprint/maps/ScagGeneralPlanParcelFeature__land_use_definition_id.cartocss new file mode 100644 index 000000000..c4c47c628 --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagGeneralPlanParcelFeature__land_use_definition_id.cartocss @@ -0,0 +1,23 @@ + +{line-color: #000000} + +[zoom<=12] {line-width:0.2;} + +[zoom=13] {line-width:0.4;} + +[zoom>=14] {line-width:0.7;} + +[zoom=16] {line-width:1;} + +[zoom>=17] {line-width:1.3;} + +[zoom>=19] {line-width:2.4;} + +[zoom>=20] {line-width:3;} + +{% for key, value in attributes.land_use_definition__id.equals.items %} +[land_use_definition__id={{ key }}] { +{% if value.fill.color %} polygon-fill: {{ value.fill.color }}; {% endif %} +{% if value.outline.color %}line-color: {{ value.outline.color }}; {% endif %} + +polygon-opacity: {% if value.fill.opacity %} {{ value.fill.opacity }}{% else %}.6{% endif %};} {% endfor %} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/ScagGeneralPlanParcelFeature__land_use_definition_id.css b/footprint/main/templates/footprint/maps/ScagGeneralPlanParcelFeature__land_use_definition_id.css new file mode 100644 index 000000000..be8cd1ea5 --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagGeneralPlanParcelFeature__land_use_definition_id.css @@ -0,0 +1,7 @@ +{% for key, value in land_use_definition.equals.items %}.lu-{{ key }} { + + {% if value.fill.color %} fill: {{ value.fill.color }}; {% endif %} + {% if value.fill.opacity %} opacity: {{ value.fill.opacity }}; {% endif %} + stroke: {% if value.outline.color %} {{ value.outline.color }}; + {% else %} #FFFFFF {% endif %} +} {% endfor %} diff --git a/footprint/main/templates/footprint/maps/ScagHabitatConservationAreasFeature.cartocss b/footprint/main/templates/footprint/maps/ScagHabitatConservationAreasFeature.cartocss new file mode 100644 index 000000000..6a69cd21a --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagHabitatConservationAreasFeature.cartocss @@ -0,0 +1,17 @@ + +{buffer-size: 50;} + +[zoom=10] { + line-color:#E68A00; + line-width:3; +} + +[zoom=11] { + line-color:#E68A00; + line-width:3.6; +} + +[zoom>=12] { + line-color:#E68A00; + line-width:4; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/ScagHabitatConservationAreasFeature.css b/footprint/main/templates/footprint/maps/ScagHabitatConservationAreasFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/ScagJurisdictionBoundaryFeature.cartocss b/footprint/main/templates/footprint/maps/ScagJurisdictionBoundaryFeature.cartocss new file mode 100644 index 000000000..b4723290f --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagJurisdictionBoundaryFeature.cartocss @@ -0,0 +1,84 @@ + +{buffer-size: 50;} + +@citywidth: 2.5; +@citydash: 5; +@cityspace: 4; + +[zoom<=9] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 0.4; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFFFFF; + mainline/line-width: @citywidth * 0.2; + mainline/line-dasharray: @citydash * 0.5, @cityspace * 0.5; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=10] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 1; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFFFFF; + mainline/line-width: @citywidth * 0.5; + mainline/line-dasharray: @citydash * 0.5, @cityspace * 0.5; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom<=12] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 3; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFFFFF; + mainline/line-width: @citywidth * 1; + mainline/line-dasharray: @citydash * 1, @cityspace * 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=13] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 4; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFFFFF; + mainline/line-width: @citywidth * 1.4; + mainline/line-dasharray: @citydash * 1.4, @cityspace * 1.4; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=14] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFFFFF; + mainline/line-width: @citywidth * 1.7; + mainline/line-dasharray: @citydash * 1.7, @cityspace * 1.7; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom>=15] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @citywidth * 6; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFFFFF; + mainline/line-width: @citywidth * 2.5; + mainline/line-dasharray: @citydash * 2.5, @cityspace * 2.5; + mainline/line-join: round; + mainline/line-cap: round; +} diff --git a/footprint/main/templates/footprint/maps/ScagJurisdictionBoundaryFeature.css b/footprint/main/templates/footprint/maps/ScagJurisdictionBoundaryFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/ScagParksOpenSpaceFeature.cartocss b/footprint/main/templates/footprint/maps/ScagParksOpenSpaceFeature.cartocss new file mode 100644 index 000000000..f148f3431 --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagParksOpenSpaceFeature.cartocss @@ -0,0 +1,23 @@ + +{buffer-size: 50;} + +[zoom=12] { + line-color:#594; + line-width:0.5; + polygon-opacity:0.6; + polygon-fill:#ae8; +} + +[zoom=13] { + line-color:#594; + line-width:1; + polygon-opacity:0.6; + polygon-fill:#ae8; +} + +[zoom>=14] { + line-color:#594; + line-width:1; + polygon-opacity:0.6; + polygon-fill:#ae8; +} diff --git a/footprint/main/templates/footprint/maps/ScagParksOpenSpaceFeature.css b/footprint/main/templates/footprint/maps/ScagParksOpenSpaceFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/ScagPrimarySPZFeature.cartocss b/footprint/main/templates/footprint/maps/ScagPrimarySPZFeature.cartocss new file mode 100644 index 000000000..9aad9d774 --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagPrimarySPZFeature.cartocss @@ -0,0 +1,106 @@ + +{buffer-size: 50;} + +[zoom<=13] { +/* outline/line-width: 1.3; + outline/line-color:#FFFFFF; */ + mainline/line-width: 1; + mainline/line-color:#E60000; + mainline/line-join: round; + mainline/line-cap: round;} + +[zoom=14] { +/* outline/line-width: 2.4; + outline/line-color:#FFFFFF; */ + mainline/line-width: 1.4; + mainline/line-color:#E60000; + mainline/line-join: round; + mainline/line-cap: round;} + +[zoom=15] { +/* glow/line-opacity:0.2; + glow/line-width: 5; + glow/line-color:#FFFFFF; + outline/opacity: 0.8; + outline/line-width: 3.6; + outline/line-color:#FFFFFF; */ + mainline/opacity: 1; + mainline/line-width: 2; + mainline/line-color:#E60000; + mainline/line-join: round; + mainline/line-cap: round; +/* ::label { + text-name:"[spzid]"; + text-face-name:"DejaVu Serif Condensed Bold"; + text-halo-fill:#ffffff; + text-halo-radius:0.9; + text-size:10; + text-allow-overlap:false; + text-placement:interior;} */ + } + +[zoom=16] { +/* glow/line-opacity:0.3; + glow/line-width: 7; + glow/line-color:#FFFFFF; + outline/opacity: 0.8; + outline/line-width: 6.2; + outline/line-color:#FFFFFF; */ + mainline/opacity: 1; + mainline/line-width: 3.1; + mainline/line-color:#E60000; + mainline/line-join: round; + mainline/line-cap: round; +/* ::label { + text-name:"[spzid]"; + text-face-name:"DejaVu Serif Condensed Bold"; + text-halo-fill:#ffffff; + text-halo-radius:1.3; + text-size:12; + text-allow-overlap:false; + text-placement:interior;} */ + } + +[zoom=17] { +/* glow/line-opacity:0.3; + glow/line-width: 12; + glow/line-color:#FFFFFF; + outline/opacity: 0.8; + outline/line-width: 8.4; + outline/line-color:#FFFFFF; */ + mainline/opacity: 1; + mainline/line-width: 4; + mainline/line-color:#E60000; + mainline/line-join: round; + mainline/line-cap: round; + /* ::label { + text-name:"[spzid]"; + text-face-name:"DejaVu Serif Condensed Bold"; + text-halo-fill:#ffffff; + text-halo-radius:1.5; + text-size:14; + text-allow-overlap:false; + text-placement:interior;} */ + } + +[zoom>=18] { +/* glow/line-opacity:0.3; + glow/line-width: 15; + glow/line-color:#FFFFFF; + outline/opacity: 0.8; + outline/line-width: 10; + outline/line-color:#FFFFFF; */ + mainline/opacity: 1; + mainline/line-width: 5; + mainline/line-color:#E60000; + mainline/line-join: round; + mainline/line-cap: round; + /* ::label { + text-name:"[spzid]"; + text-face-name:"DejaVu Serif Condensed Bold"; + text-halo-fill:#ffffff; + text-halo-radius:2; + text-size:18; + text-allow-overlap:false; + text-placement:interior;} */ + } diff --git a/footprint/main/templates/footprint/maps/ScagPrimarySPZFeature.css b/footprint/main/templates/footprint/maps/ScagPrimarySPZFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/ScagSphereOfInfluenceFeature.cartocss b/footprint/main/templates/footprint/maps/ScagSphereOfInfluenceFeature.cartocss new file mode 100644 index 000000000..adb2a80a1 --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagSphereOfInfluenceFeature.cartocss @@ -0,0 +1,62 @@ + +{buffer-size: 50;} + +@SOIwidth: 2.5; +@SOIdash: 3; +@SOIspace: 4; + +[zoom<=12] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @SOIwidth * 2; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFCC00; + mainline/opacity: 0.8; + mainline/line-width: @SOIwidth * 1; + mainline/line-dasharray: @SOIdash * 1, @SOIspace * 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=13] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @SOIwidth * 4; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFCC00; + mainline/opacity: 0.8; + mainline/line-width: @SOIwidth * 1.5; + mainline/line-dasharray: @SOIdash * 1.5, @SOIspace * 1.5; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=14] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @SOIwidth * 4.2; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFCC00; + mainline/opacity: 0.8; + mainline/line-width: @SOIwidth * 1.8; + mainline/line-dasharray: @SOIdash * 1.8, @SOIspace * 1.8; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom>=15] { + outline/line-color:#000000; + outline/line-opacity: 0.4; + outline/line-width: @SOIwidth * 4.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-color:#FFCC00; + mainline/opacity: 0.8; + mainline/line-width: @SOIwidth * 2.5; + mainline/line-dasharray: @SOIdash * 2.5, @SOIspace * 2.5; + mainline/line-join: round; + mainline/line-cap: round; +} diff --git a/footprint/main/templates/footprint/maps/ScagSphereOfInfluenceFeature.css b/footprint/main/templates/footprint/maps/ScagSphereOfInfluenceFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/ScagTier1TazFeature.cartocss b/footprint/main/templates/footprint/maps/ScagTier1TazFeature.cartocss new file mode 100644 index 000000000..4ed7ff7c0 --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagTier1TazFeature.cartocss @@ -0,0 +1,79 @@ + +{buffer-size: 50;} + +[zoom=11] { + outline/line-width: 2; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; mainline/line-width: 1.4; + mainline/line-color:#000000; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=12] { + outline/line-width: 2; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 1.3; + mainline/line-color:#000000; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=13] { + outline/line-width: 3; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 1.4; + mainline/line-color:#000000; + mainline/line-opacity: 2; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=14] { + outline/line-width: 5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 3; + mainline/line-color:#000000; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=15] { + outline/line-width: 6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 4; + mainline/line-color:#000000; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom>=16] { + outline/line-width: 10; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 6; + mainline/line-color:#000000; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/ScagTier1TazFeature.css b/footprint/main/templates/footprint/maps/ScagTier1TazFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/ScagTier2TazFeature.cartocss b/footprint/main/templates/footprint/maps/ScagTier2TazFeature.cartocss new file mode 100644 index 000000000..c7c38d881 --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagTier2TazFeature.cartocss @@ -0,0 +1,67 @@ + +{buffer-size: 50;} + +[zoom=12] { + outline/line-width: 1.5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 1.3; + mainline/line-color:#00CC99; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=13] { + outline/line-width: 2; + outline/line-color:#ffffff; + outline/line-opacity: 0.3; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 1.4; + mainline/line-color:#00CC99; + mainline/line-opacity: 2; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=14] { + outline/line-width: 4; + outline/line-color:#ffffff; + outline/line-opacity: 0.3; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 3; + mainline/line-color:#00CC99; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom=15] { + outline/line-width: 5.5; + outline/line-color:#ffffff; + outline/line-opacity: 0.3; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 4; + mainline/line-color:#00CC99; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} + +[zoom>=16] { + outline/line-width: 8; + outline/line-color:#ffffff; + outline/line-opacity: 0.3; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 5; + mainline/line-color:#00CC99; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/ScagTier2TazFeature.css b/footprint/main/templates/footprint/maps/ScagTier2TazFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/ScagTransitAreasFeature.cartocss b/footprint/main/templates/footprint/maps/ScagTransitAreasFeature.cartocss new file mode 100644 index 000000000..6d5a241ff --- /dev/null +++ b/footprint/main/templates/footprint/maps/ScagTransitAreasFeature.cartocss @@ -0,0 +1,74 @@ + +{buffer-size: 50;} + +[zoom<12] { + outline/line-width: 1.6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; mainline/line-width: 1; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; + polygon-fill:#CC00FF; + polygon-opacity:0.15; +} + +[zoom=12] { + outline/line-width: 3.8; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 1.8; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; + polygon-fill:#CC00FF; + polygon-opacity:0.1; +} + +[zoom=13] { + outline/line-width: 5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + mainline/line-width: 2.6; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} + +[zoom=14] { + outline/line-width: 6; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 2.8; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} + +[zoom>=15] { + outline/line-width: 7.5; + outline/line-color:#ffffff; + outline/line-opacity: 0.5; + outline/line-join: round; + outline/line-cap: round; + mainline/line-width: 3.5; + mainline/line-color:#CC00FF; + mainline/line-opacity: 1; + mainline/line-join: round; + mainline/line-cap: round; + polygon-fill:#CC00FF; + polygon-opacity:0.05; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/ScagTransitAreasFeature.css b/footprint/main/templates/footprint/maps/ScagTransitAreasFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/maps/VmtFeature.cartocss b/footprint/main/templates/footprint/maps/VmtFeature.cartocss new file mode 100644 index 000000000..7fc216d3d --- /dev/null +++ b/footprint/main/templates/footprint/maps/VmtFeature.cartocss @@ -0,0 +1,40 @@ +.{{ htmlClass }} +{buffer-size: 50;} + + + +[ vmt_daily_per_hh > 60] { + polygon-fill:#C80000; + line-color: #909090; + line-width:.3; +} + +[ vmt_daily_per_hh <= 60] { + polygon-fill:#FF9900; + line-color: #909090; + line-width:.3; +} + +[ vmt_daily_per_hh <= 40] { + polygon-fill:#FFFF00; + line-color: #909090; + line-width:.3; +} + +[ vmt_daily_per_hh <= 30] { + polygon-fill:#00CC33; + line-color: #909090; + line-width:.3; +} + +[ vmt_daily_per_hh <= 20] { + polygon-fill:#009933; + line-color: #909090; + line-width:.3; +} + +[ vmt_daily_per_hh = 0] { + polygon-fill:#E8E8E8; + line-color: #909090; + line-width:.3; +} \ No newline at end of file diff --git a/footprint/main/templates/footprint/maps/VmtFeature.css b/footprint/main/templates/footprint/maps/VmtFeature.css new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/templates/footprint/negative.html b/footprint/main/templates/footprint/negative.html new file mode 100644 index 000000000..91751b254 --- /dev/null +++ b/footprint/main/templates/footprint/negative.html @@ -0,0 +1,879 @@ + + + + + + //d3.select(self.frameElement).style("height", "1000px"); + //d3.select(self.frameElement).style("width", "1000px"); + barStack + + + + + + + + \ No newline at end of file diff --git a/footprint/main/templates/footprint/polymap.html b/footprint/main/templates/footprint/polymap.html new file mode 100644 index 000000000..16330c27e --- /dev/null +++ b/footprint/main/templates/footprint/polymap.html @@ -0,0 +1,140 @@ + + + + Urban Footprint Map Development + + + + + + + + + + + + + + + + + + + + + + + {% csrf_token %} + +{% load tilestache_layers %} +
    + + + + + + + + \ No newline at end of file diff --git a/footprint/main/templates/footprint/tests/test.html b/footprint/main/templates/footprint/tests/test.html new file mode 100644 index 000000000..869bcd117 --- /dev/null +++ b/footprint/main/templates/footprint/tests/test.html @@ -0,0 +1,20 @@ + + + + + QUnit Example + + +
    +
    + + + + + + + + + + + diff --git a/footprint/main/templates/footprint/upload.html b/footprint/main/templates/footprint/upload.html new file mode 100644 index 000000000..5e36573ec --- /dev/null +++ b/footprint/main/templates/footprint/upload.html @@ -0,0 +1,9 @@ +
    + + {{form.as_table}} +
    + + +
    + + diff --git a/footprint/main/tests/__init__.py b/footprint/main/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/fixtures/__init__.py b/footprint/main/tests/fixtures/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/fixtures/client/__init__.py b/footprint/main/tests/fixtures/client/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/fixtures/client/default/__init__.py b/footprint/main/tests/fixtures/client/default/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/fixtures/client/default/config_entity/__init__.py b/footprint/main/tests/fixtures/client/default/config_entity/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/fixtures/client/default/config_entity/config_entity_fixtures.py b/footprint/main/tests/fixtures/client/default/config_entity/config_entity_fixtures.py new file mode 100644 index 000000000..2a0bdc655 --- /dev/null +++ b/footprint/main/tests/fixtures/client/default/config_entity/config_entity_fixtures.py @@ -0,0 +1,119 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.geos import MultiPolygon, Polygon +from footprint.client.configuration.fixture import ConfigEntitiesFixture +from footprint.main.mixins.category import Category +from django.conf import settings + +__author__ = 'calthorpe_associates' + +config_entity = ConfigEntitiesFixture( + regions=[ + { + 'key': 'eastbaywest', + 'name': 'East Bay West', + 'description': 'Alameda is possibly the best county in the Bay Area. It contains sweeping vistas, intellectual firepower, and hip culture', + 'bounds': MultiPolygon([Polygon( + ((-122.719, 37.394), # bottom left + (-122.719, 38.059), # top left + (-121.603, 38.059), # top right + (-121.603, 37.394), # bottom right + (-122.719, 37.394), # bottom left + ))]) + }, + { + 'key': 'eastbayeast', + 'name': 'East Bay East', + 'description': "Contra Costa county is so named for its juxtaposition with San Francisco", + 'bounds': MultiPolygon([Polygon( + ((-122.4319, 37.7182), # bottom left + (-122.4319, 38.1002), # top left + (-121.5356, 38.1002), # top right + (-121.5356, 37.7182), # bottom right + (-122.4319, 37.7182), # bottom left + ))]) + } + ], + + projects=[ + { + 'key': 'alameda', + 'name': 'Alameda County', + 'description': 'This project is aimed at making Alameda county event cooler', + 'base_table': settings.STATIC_ROOT + 'sample_data/eastbaywest_alameda_base', + + 'base_year': 2012, + 'region_index': 0, + 'bounds': MultiPolygon([Polygon( + ((-122.719, 37.394), # bottom left + (-122.719, 38.059), # top left + (-121.603, 38.059), # top right + (-121.603, 37.394), # bottom right + (-122.719, 37.394), # bottom left + ))]) + }, + { + 'key': 'contracosta', + 'name': 'Contra Costa County', + 'description': "This project intents to explore densification options around the county's train stations", + 'base_year': 2012, + 'region_index': 1, + 'bounds': MultiPolygon([Polygon( + ((-122.4319, 37.7182), # bottom left + (-122.4319, 38.1002), # top left + (-121.5356, 38.1002), # top right + (-121.5356, 37.7182), # bottom right + (-122.4319, 37.7182), # bottom left + ))]) + } + ], + + scenarios=[ + { + 'key': 'smart', + 'name': 'Smart Growth Scenario', + 'description': 'This scenario is aimed at making Alameda county grow up smart', + 'year': 2030, + 'project_index': 0, + 'categories': [Category(key='category', value='smart'), Category(key='coastal', value='yes')] + }, + { + 'key': 'trend', + 'name': 'Trend Scenario', + 'description': "This business as usual scenario is Alameda county's road to mediocrity", + 'year': 2030, + 'project_index': 0, + 'categories': [Category(key='category', value='dumb'), Category(key='coastal', value='yes')] + }, + { + 'key': 'smart_cc', + 'name': 'Smart Growth Scenario', + 'description': 'This scenario is aimed at making Contra Costa county grow up smart', + 'year': 2030, + 'project_index': 1, + 'categories': [Category(key='category', value='smart'), Category(key='coastal', value='no')] + }, + { + 'key': 'trend_cc', + 'name': 'Trend Scenario', + 'description': "This business as usual scenario is Contra Costa's road to mediocrity", + 'year': 2030, + 'project_index': 1, + 'categories': [Category(key='category', value='dumb'), Category(key='coastal', value='no')] + } + ]) diff --git a/footprint/main/tests/fixtures/client/default/db_entities.py b/footprint/main/tests/fixtures/client/default/db_entities.py new file mode 100644 index 000000000..82e1b9d7f --- /dev/null +++ b/footprint/main/tests/fixtures/client/default/db_entities.py @@ -0,0 +1,81 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import glob +import datetime +import os +import geojson +from footprint.main.models.geospatial.geojson_feature import GeoJsonFeature +from django.contrib.gis.geos import GEOSGeometry +from jsonify.templatetags.jsonify import jsonify +from django.conf import settings +from footprint.main.utils.dynamic_subclassing import dynamic_model_class + +__author__ = 'calthorpe_associates' + +class SampleDbEntityData(): + def geojson_feature_classes(self, schema='public', **kwargs): + """ + Creates various GeojsonFeature classes by importing geojson and saving it to the database via a dynamic subclass of GeojsonFeature + :schema: The optional schema to use for the dynamic subclass's meta db_table attribute, which will allow the class's table to be saved in the specified schema. Defaults to public + :param kwargs use 'limit' to limit the number of classes created + :return: a list of lists. Each list is a list of features of distinct subclass of GeoJsonFeature that is created dynamically. To persist these features, you must first create the subclass's table in the database using create_table_for_dynamic_class(). You should also register the table as a DbEntity. + """ + def parse(file): + """ + Parses a geojosn file and dynamically creates a subclass of GeoJsonFeature + :param file: a filepath + :return: a list of instances of the dynamically created subclass. The class must be saved to the database using create_table_for_dynamic_class() prior to persisting the instances. To access the class, simply get the class of the first instance, since they are all identical + """ + + fp = open(file) + data = geojson.load(fp, object_hook=geojson.GeoJSON.to_instance) + dynamic_table_name = "{0}{1}".format(os.path.splitext(os.path.basename(file))[0], formatted_timestamp()) + GeoJsonFeatureSubclass = dynamic_model_class( + GeoJsonFeature, + schema, + dynamic_table_name, + {}) + + def instantiate_sub_class(feature): + """ + Instantiates an instance of the dynamic subclass of GeoJsonFeature based on the given feature. + :param feature: A feature parsed django-geojson. The feature is actually reserialized to json in order to construct a GEOSGeometry instance. + :return: An instance of the GeoJsonFeature subclass, which contains the geometry, properties of the feature, and perhaps the crs + """ + # TODO, crs should be read from the geojson when present. + # This crs isn't actually picked up by the GEOSGeometry constructor + srid = settings.SRID_PREFIX.format(settings.DEFAULT_SRID) + crs = { + "type": "name", + "properties": { + "name": srid + } + } + # Ironically, we have to rejsonify the data so that GEOSGeometry can parse the feature as json + json = jsonify({'type':feature.geometry.type, 'coordinates':feature.geometry.coordinates, 'crs':crs}) + geometry = GEOSGeometry(json) + properties = feature.properties + return GeoJsonFeatureSubclass(geometry=geometry, properties=properties) + + return map(lambda feature: instantiate_sub_class(feature), data.features) + + return map(lambda file: parse(file), glob.glob("main/tests/test_data/sample_geojson/*.json")[:kwargs.get('limit',None)]) + +def formatted_timestamp(): + return datetime.datetime.now().strftime("%S%f") + diff --git a/footprint/main/tests/fixtures/client/default/fixtures.py b/footprint/main/tests/fixtures/client/default/fixtures.py new file mode 100644 index 000000000..272e069a0 --- /dev/null +++ b/footprint/main/tests/fixtures/client/default/fixtures.py @@ -0,0 +1,23 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from footprint.client.configuration.fixture import FootprintFixture + +from config_entity.config_entity_fixtures import config_entity +# Use the real built_form and policy fixtures +from footprint.client.configuration.default import built_form, policy + +footprint_fixture = FootprintFixture( + config_entity=config_entity, + built_form=built_form, + policy=policy +) diff --git a/footprint/main/tests/fixtures/client/default/media.py b/footprint/main/tests/fixtures/client/default/media.py new file mode 100644 index 000000000..59f11ea2f --- /dev/null +++ b/footprint/main/tests/fixtures/client/default/media.py @@ -0,0 +1,40 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + +media = [ + { + 'key': 'logo', + 'name': 'The logo for this product', + 'description': 'The logo can be featured somewhere prominent', + 'url': 'http://logosthatmatter.lock', + 'content_type': Keys.CONTENT_TYPE_PNG, + 'content':'gobblygook' + }, + { + 'key': 'audio_clip', + 'name': 'The perfect sound for this product', + 'description': 'This audio clip should be played immediately', + 'url': 'http://audiofromparadise.net', + 'content_type': 'mp3', + 'content':'noisygobblygook' + }, + ] + diff --git a/footprint/main/tests/fixtures/robot.py b/footprint/main/tests/fixtures/robot.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_client/__init__.py b/footprint/main/tests/test_client/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_client/sacog/__init__.py b/footprint/main/tests/test_client/sacog/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_client/sacog/test_sacog_footprint_init.py b/footprint/main/tests/test_client/sacog/test_sacog_footprint_init.py new file mode 100644 index 000000000..15a6dc91d --- /dev/null +++ b/footprint/main/tests/test_client/sacog/test_sacog_footprint_init.py @@ -0,0 +1,15 @@ +from django.core.management import call_command +from django.test.utils import override_settings +from nose import with_setup +from footprint.main.tests.test_client.test_client_footprint_init import TestClientFootprintInit +from footprint import settings + +__author__ = 'calthorpe_associates' + +class TestScagFootprintInit(TestClientFootprintInit): + + def test_init(self): + super(TestSacogFootprintInit, self).test_init() + #raise Exception(settings.CLIENT) + call_command('footprint_init') + assert() diff --git a/footprint/main/tests/test_client/scag/__init__.py b/footprint/main/tests/test_client/scag/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_client/scag/test_scag_footprint_init.py b/footprint/main/tests/test_client/scag/test_scag_footprint_init.py new file mode 100644 index 000000000..520930497 --- /dev/null +++ b/footprint/main/tests/test_client/scag/test_scag_footprint_init.py @@ -0,0 +1,14 @@ +from django.core.management import call_command +from footprint.main.models import Scenario +from footprint.main.models.keys.keys import Keys +from footprint.main.tests.test_client.test_client_footprint_init import TestClientFootprintInit + +__author__ = 'calthorpe_associates' + +class TestScagFootprintInit(TestClientFootprintInit): + + def test_init(self): + super(TestScagFootprintInit, self).test_init() + #raise Exception(settings.CLIENT) + call_command('footprint_init') + assert(Scenario.objects.all()[0].feature_class_of_db_entity_key(Keys.DB_ABSTRACT_PRIMARY_PARCEL_SOURCE).objects.count() > 0) diff --git a/footprint/main/tests/test_data/__init__.py b/footprint/main/tests/test_data/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_data/geo_django_data/cities/cities.dbf b/footprint/main/tests/test_data/geo_django_data/cities/cities.dbf new file mode 100644 index 000000000..8b276331b Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/cities/cities.dbf differ diff --git a/footprint/main/tests/test_data/geo_django_data/cities/cities.prj b/footprint/main/tests/test_data/geo_django_data/cities/cities.prj new file mode 100644 index 000000000..a30c00a55 --- /dev/null +++ b/footprint/main/tests/test_data/geo_django_data/cities/cities.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/footprint/main/tests/test_data/geo_django_data/cities/cities.shp b/footprint/main/tests/test_data/geo_django_data/cities/cities.shp new file mode 100644 index 000000000..1c46ccc5f Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/cities/cities.shp differ diff --git a/footprint/main/tests/test_data/geo_django_data/cities/cities.shx b/footprint/main/tests/test_data/geo_django_data/cities/cities.shx new file mode 100644 index 000000000..6be3fd68e Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/cities/cities.shx differ diff --git a/footprint/main/tests/test_data/geo_django_data/counties/counties.dbf b/footprint/main/tests/test_data/geo_django_data/counties/counties.dbf new file mode 100644 index 000000000..ccdbb264b Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/counties/counties.dbf differ diff --git a/footprint/main/tests/test_data/geo_django_data/counties/counties.shp b/footprint/main/tests/test_data/geo_django_data/counties/counties.shp new file mode 100644 index 000000000..2e7dca5e4 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/counties/counties.shp differ diff --git a/footprint/main/tests/test_data/geo_django_data/counties/counties.shx b/footprint/main/tests/test_data/geo_django_data/counties/counties.shx new file mode 100644 index 000000000..46ed3bb20 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/counties/counties.shx differ diff --git a/footprint/main/tests/test_data/geo_django_data/geometries.json.gz b/footprint/main/tests/test_data/geo_django_data/geometries.json.gz new file mode 100644 index 000000000..683dc83e4 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/geometries.json.gz differ diff --git a/footprint/main/tests/test_data/geo_django_data/interstates/interstates.dbf b/footprint/main/tests/test_data/geo_django_data/interstates/interstates.dbf new file mode 100644 index 000000000..a88d17158 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/interstates/interstates.dbf differ diff --git a/footprint/main/tests/test_data/geo_django_data/interstates/interstates.prj b/footprint/main/tests/test_data/geo_django_data/interstates/interstates.prj new file mode 100644 index 000000000..a30c00a55 --- /dev/null +++ b/footprint/main/tests/test_data/geo_django_data/interstates/interstates.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/footprint/main/tests/test_data/geo_django_data/interstates/interstates.shp b/footprint/main/tests/test_data/geo_django_data/interstates/interstates.shp new file mode 100644 index 000000000..6d93de75e Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/interstates/interstates.shp differ diff --git a/footprint/main/tests/test_data/geo_django_data/interstates/interstates.shx b/footprint/main/tests/test_data/geo_django_data/interstates/interstates.shx new file mode 100644 index 000000000..7b9088ac5 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/interstates/interstates.shx differ diff --git a/footprint/main/tests/test_data/geo_django_data/invalid/emptypoints.dbf b/footprint/main/tests/test_data/geo_django_data/invalid/emptypoints.dbf new file mode 100644 index 000000000..aa2109047 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/invalid/emptypoints.dbf differ diff --git a/footprint/main/tests/test_data/geo_django_data/invalid/emptypoints.shp b/footprint/main/tests/test_data/geo_django_data/invalid/emptypoints.shp new file mode 100644 index 000000000..bdcfb83be Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/invalid/emptypoints.shp differ diff --git a/footprint/main/tests/test_data/geo_django_data/invalid/emptypoints.shx b/footprint/main/tests/test_data/geo_django_data/invalid/emptypoints.shx new file mode 100644 index 000000000..dea663eb0 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/invalid/emptypoints.shx differ diff --git a/footprint/main/tests/test_data/geo_django_data/test_point/test_point.dbf b/footprint/main/tests/test_data/geo_django_data/test_point/test_point.dbf new file mode 100644 index 000000000..b2b4ecaea Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/test_point/test_point.dbf differ diff --git a/footprint/main/tests/test_data/geo_django_data/test_point/test_point.prj b/footprint/main/tests/test_data/geo_django_data/test_point/test_point.prj new file mode 100644 index 000000000..a30c00a55 --- /dev/null +++ b/footprint/main/tests/test_data/geo_django_data/test_point/test_point.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/footprint/main/tests/test_data/geo_django_data/test_point/test_point.shp b/footprint/main/tests/test_data/geo_django_data/test_point/test_point.shp new file mode 100644 index 000000000..95e8b0ac9 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/test_point/test_point.shp differ diff --git a/footprint/main/tests/test_data/geo_django_data/test_point/test_point.shx b/footprint/main/tests/test_data/geo_django_data/test_point/test_point.shx new file mode 100644 index 000000000..087f3da1f Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/test_point/test_point.shx differ diff --git a/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.dbf b/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.dbf new file mode 100644 index 000000000..7965bd614 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.dbf differ diff --git a/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.prj b/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.prj new file mode 100644 index 000000000..a30c00a55 --- /dev/null +++ b/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]] \ No newline at end of file diff --git a/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.shp b/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.shp new file mode 100644 index 000000000..b22930bd2 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.shp differ diff --git a/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.shx b/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.shx new file mode 100644 index 000000000..c92f78bd4 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/test_poly/test_poly.shx differ diff --git a/footprint/main/tests/test_data/geo_django_data/test_vrt/test_vrt.csv b/footprint/main/tests/test_data/geo_django_data/test_vrt/test_vrt.csv new file mode 100644 index 000000000..dff648ffe --- /dev/null +++ b/footprint/main/tests/test_data/geo_django_data/test_vrt/test_vrt.csv @@ -0,0 +1,4 @@ +POINT_X,POINT_Y,NUM +1.0,2.0,5 +5.0,23.0,17 +100.0,523.5,23 diff --git a/footprint/main/tests/test_data/geo_django_data/test_vrt/test_vrt.vrt b/footprint/main/tests/test_data/geo_django_data/test_vrt/test_vrt.vrt new file mode 100644 index 000000000..979c179bb --- /dev/null +++ b/footprint/main/tests/test_data/geo_django_data/test_vrt/test_vrt.vrt @@ -0,0 +1,7 @@ + + +test_vrt.csv +wkbPoint25D + + + \ No newline at end of file diff --git a/footprint/main/tests/test_data/geo_django_data/texas.dbf b/footprint/main/tests/test_data/geo_django_data/texas.dbf new file mode 100644 index 000000000..827c06507 Binary files /dev/null and b/footprint/main/tests/test_data/geo_django_data/texas.dbf differ diff --git a/footprint/main/tests/test_data/imported_building.py b/footprint/main/tests/test_data/imported_building.py new file mode 100644 index 000000000..5ef210845 --- /dev/null +++ b/footprint/main/tests/test_data/imported_building.py @@ -0,0 +1,108 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from csvImporter.fields import CharField, FloatField, IntegerField +from csvImporter.model import CsvModel + +__author__ = 'calthorpe_associates' + +class ImportedBuilding(CsvModel): + #source,hyperlink,building_type, + source = CharField(prepare=lambda x: x or '') + hyperlink = CharField(prepare=lambda x: x or '') + building_type = CharField(prepare=lambda x: x or '') + #building,household_size, + name = CharField(prepare=lambda x: x or '') + household_size = FloatField(prepare=lambda x: x or 0) + + #all_uses, + all_uses = CharField(prepare=lambda x: x or '') + +#Pct_SF_Large_Lot,Pct_SF_Small_Lot,Pct_Attached_SF,Pct_MF_2_to_4,Pct_MF_5_Plus, + percent_single_family_large_lot = FloatField(prepare=lambda x: x or 0) + percent_single_family_small_lot = FloatField(prepare=lambda x: x or 0) + percent_attached_single_family = FloatField(prepare=lambda x: x or 0) + percent_multifamily_2_to_4 = FloatField(prepare=lambda x: x or 0) + percent_multifamily_5_plus = FloatField(prepare=lambda x: x or 0) + +# Pct_Emp_Office_Svc,Pct_Educ_Svc,Pct_Medical_Svc,Pct_Public_Admin, + percent_office_services = FloatField(prepare=lambda x: x or 0) + percent_education_services = FloatField(prepare=lambda x: x or 0) + percent_medical_services = FloatField(prepare=lambda x: x or 0) + percent_public_admin = FloatField(prepare=lambda x: x or 0) + +#Pct_Retail_Svc,Pct_Restuarant,Pct_Accommodation,Pct_Arts_Entertainment,Pct_Other_Svc, + percent_retail_services = FloatField(prepare=lambda x: x or 0) + percent_restaurant = FloatField(prepare=lambda x: x or 0) + percent_accommodation = FloatField(prepare=lambda x: x or 0) + percent_arts_entertainment = FloatField(prepare=lambda x: x or 0) + percent_other_services = FloatField(prepare=lambda x: x or 0) + +# Pct_Manufacturing,Pct_Transport_warehouse,Pct_Wholesale,Pct_Construction_Util,Pct_Agriculture,Pct_Extraction, + percent_manufacturing = FloatField(prepare=lambda x: x or 0) + percent_transport_warehouse = FloatField(prepare=lambda x: x or 0) + percent_wholesale = FloatField(prepare=lambda x: x or 0) + percent_construction_utilities = FloatField(prepare=lambda x: x or 0) + + percent_agriculture = FloatField(prepare=lambda x: x or 0) + percent_extraction = FloatField(prepare=lambda x: x or 0) + +# percent_of_building_type,floors,percent_residential,percent_retail,percent_office,percent_industrial, + percent_of_building_type = FloatField(prepare=lambda x: x or 0) + floors = FloatField(prepare=lambda x: x or 0) + + percent_residential = FloatField(prepare=lambda x: x or 0) + percent_retail = FloatField(prepare=lambda x: x or 0) + percent_office = FloatField(prepare=lambda x: x or 0) + percent_industrial = FloatField(prepare=lambda x: x or 0) + +#total_far,parking_spaces,parking_structure_square_feet,residential_efficiency,residential_lot_square_feet,square_feet_per_du, + total_far = FloatField(prepare=lambda x: x or 0) + parking_spaces = FloatField(prepare=lambda x: x or 0) + parking_structure_square_feet = FloatField(prepare=lambda x: x or 0) + + residential_efficiency = FloatField(prepare=lambda x: x or 0) + residential_average_lot_size = FloatField(prepare=lambda x: x or 0) + residential_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + +# retail_efficiency,retail_square_feet_per_employee,office_efficiency,office_square_feet_per_employee, + retail_efficiency = FloatField(prepare=lambda x: x or 0) + retail_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) + + office_efficiency = FloatField(prepare=lambda x: x or 0) + office_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) +#industrial_efficiency,industrial_square_feet_per_employee, + industrial_efficiency = FloatField(prepare=lambda x: x or 0) + industrial_square_feet_per_unit = FloatField(prepare=lambda x: x or 0) +# misc_percent_of_retail_use,retail_percent_of_retail_use,restaurant_percent_of_retail_use,grocery_percent_of_retail_use, + misc_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + retail_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + restaurant_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) + grocery_percent_of_retail_use = FloatField(prepare=lambda x: x or 0) +# hardscape_percent,irrigated_percent,impervious_roof_percent,impervious_hardscape_percent,pervious_hardscape_percent,softscape_landscape_percent, + hardscape_percent = FloatField(prepare=lambda x: x or 0) + irrigated_percent = FloatField(prepare=lambda x: x or 0) + impervious_roof_percent = FloatField(prepare=lambda x: x or 0) + impervious_hardscape_percent = FloatField(prepare=lambda x: x or 0) + pervious_hardscape_percent = FloatField(prepare=lambda x: x or 0) + softscape_and_landscape_percent = FloatField(prepare=lambda x: x or 0) +# id + id = IntegerField(prepare=lambda x: x or 0) + + class Meta: + delimiter = "," + has_header = True diff --git a/footprint/main/tests/test_data/imported_buildingtype.py b/footprint/main/tests/test_data/imported_buildingtype.py new file mode 100644 index 000000000..bc0f095cb --- /dev/null +++ b/footprint/main/tests/test_data/imported_buildingtype.py @@ -0,0 +1,70 @@ +__author__ = 'calthorpe_associates' + +from csvImporter.fields import CharField, FloatField, IntegerField +from csvImporter.model import CsvModel + +__author__ = 'calthorpe_associates' + +class ImportedBuildingtype(CsvModel): +# BTID,Building_Type, +# Urban Mixed Use,Urban Residential,Urban Commercial,City Mixed Use,City Residential,City Commercial, +# Town Mixed Use,Town Residential,Town Commercial,Village Mixed Use,Village Residential,Village Commercial, +# Neighborhood Residential,Neighborhood Low,Office Focus,Mixed Office and R&D,Office/Industrial,Industrial Focus, +# Low-Density Employment Park,High Intensity Activity Center,Mid Intensity Activity Center, +# Low Intensity Retail-Centered N'Hood,Retail: Strip Mall/ Big Box,Industrial/Office/Res Mixed High, +# Industrial/Office/Res Mixed Low,Suburban Multifamily,Suburban Mixed Residential,Residential Subdivision, +# Large Lot Residential Area,Rural Residential,Rural Ranchettes,Rural Employment,Campus/ University, +# Institutional,Parks & Open Space,BuildingType Name,Gross_Net_Flag + category = CharField(prepare=lambda x: x or '') + btid = IntegerField(prepare=lambda x: x or 0) + color = CharField(prepare=lambda x: x or '') + name = CharField(prepare=lambda x: x or '') + urban_mixed_use = FloatField(prepare=lambda x: x or 0) + urban_residential = FloatField(prepare=lambda x: x or 0) + urban_commercial = FloatField(prepare=lambda x: x or 0) + city_mixed_use = FloatField(prepare=lambda x: x or 0) + city_residential = FloatField(prepare=lambda x: x or 0) + city_commercial = FloatField(prepare=lambda x: x or 0) + town_mixed_use = FloatField(prepare=lambda x: x or 0) + town_residential = FloatField(prepare=lambda x: x or 0) + town_commercial = FloatField(prepare=lambda x: x or 0) + village_mixed_use = FloatField(prepare=lambda x: x or 0) + village_residential = FloatField(prepare=lambda x: x or 0) + village_commercial = FloatField(prepare=lambda x: x or 0) + neighborhood_residential = FloatField(prepare=lambda x: x or 0) + neighborhood_low = FloatField(prepare=lambda x: x or 0) + office_focus = FloatField(prepare=lambda x: x or 0) + mixed_office_and_r_and_d = FloatField(prepare=lambda x: x or 0) + office_industrial = FloatField(prepare=lambda x: x or 0) + industrial_focus = FloatField(prepare=lambda x: x or 0) + low_density_employment_park = FloatField(prepare=lambda x: x or 0) + high_intensity_activity_center = FloatField(prepare=lambda x: x or 0) + mid_intensity_activity_center = FloatField(prepare=lambda x: x or 0) + low_intensity_retail_centered_neighborhood = FloatField(prepare=lambda x: x or 0) + retail_strip_mall_big_box = FloatField(prepare=lambda x: x or 0) + industrial_office_residential_mixed_high = FloatField(prepare=lambda x: x or 0) + industrial_office_residential_mixed_low = FloatField(prepare=lambda x: x or 0) + suburban_multifamily = FloatField(prepare=lambda x: x or 0) + suburban_mixed_residential = FloatField(prepare=lambda x: x or 0) + residential_subdivision = FloatField(prepare=lambda x: x or 0) + large_lot_residential = FloatField(prepare=lambda x: x or 0) + rural_residential = FloatField(prepare=lambda x: x or 0) + rural_ranchettes = FloatField(prepare=lambda x: x or 0) + rural_employment = FloatField(prepare=lambda x: x or 0) + campus_or_university = FloatField(prepare=lambda x: x or 0) + institutional = FloatField(prepare=lambda x: x or 0) + parks_and_open_space = FloatField(prepare=lambda x: x or 0) + gross_net_flag = CharField(prepare=lambda x: x or '') + + class Meta: + delimiter = "," + has_header = True + +class ImportedPlacetypes(CsvModel): + name = CharField(prepare=lambda x: x or '') + clean_name = CharField(prepare=lambda x: x or '') + color = CharField(prepare=lambda x: x or '') + + class Meta: + delimiter = "," + has_header = True \ No newline at end of file diff --git a/footprint/main/tests/test_data/placetypes.csv b/footprint/main/tests/test_data/placetypes.csv new file mode 100644 index 000000000..045f7b768 --- /dev/null +++ b/footprint/main/tests/test_data/placetypes.csv @@ -0,0 +1,35 @@ +Urban Mixed Use,urban_mixed_use,#940000 +Urban Residential,urban_residential,#CC5500 +Urban Commercial,urban_commercial,#5D2682 +City Mixed Use,city_mixed_use,#C20000 +City Residential,city_residential,#E66A05 +City Commercial,city_commercial,#862C90 +Town Mixed Use,town_mixed_use,#E61414 +Town Residential,town_residential,#FA7E0A +Town Commercial,town_commercial,#B12F9C +Village Mixed Use,village_mixed_use,#FF5252 +Village Residential,village_residential,#FF901A +Village Commercial,village_commercial,#DC41B8 +Neighborhood Residential,neighborhood_residential,#FFA733 +Neighborhood Low,neighborhood_low,#FFBA42 +Office Focus,office_focus,#8764A6 +Mixed Office and R&D,mixed_office_and_r_and_d,#A981D5 +Office/Industrial,office_industrial,#CF9DEC +Industrial Focus,industrial_focus,#E6CCFF +Low-Density Employment Park,low_density_employment_park,#EEE5FF +High Intensity Activity Center,high_intensity_activity_center,#F679AB +Mid Intensity Activity Center,mid_intensity_activity_center,#FF9B99 +Low Intensity Retail-Centered N'Hood,low_intensity_retail_centered_neighborhood,#FFB0AD +Retail: Strip Mall/ Big Box,retail_strip_mall_big_box,#FFCCCC +Industrial/Office/Res Mixed High,industrial_office_residential_mixed_high,#FBC781 +Industrial/Office/Res Mixed Low,industrial_office_residential_mixed_low,#FCDBAE +Suburban Multifamily,suburban_multifamily,#FECF58 +Suburban Mixed Residential,suburban_mixed_residential,#FEE567 +Residential Subdivision,residential_subdivision,#FEF295 +Large Lot Residential Area,large_lot_residential,#FEF6A9 +Rural Residential,rural_residential,#EBE07A +Rural Ranchettes,rural_ranchettes,#DCC156 +Rural Employment,rural_employment,#BCD397 +Campus/ University,campus_or_university,#365F91 +Institutional,institutional,#538DD5 +Parks & Open Space,parks_and_open_space,#7DBD4C diff --git a/footprint/main/tests/test_data/sample_built_form_sets.py b/footprint/main/tests/test_data/sample_built_form_sets.py new file mode 100644 index 000000000..6950d177a --- /dev/null +++ b/footprint/main/tests/test_data/sample_built_form_sets.py @@ -0,0 +1,394 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import os +from collections import defaultdict +from footprint.main.models import PlacetypeComponent, PrimaryComponent +from footprint.main.models.built_form.placetype_component import PlacetypeComponentCategory + +from footprint.main.tests.test_data.imported_building import ImportedBuilding +from footprint.main.tests.test_data.imported_buildingtype import ImportedBuildingtype, ImportedPlacetypes + +from footprint.main.lib.functions import map_to_dict +from footprint.main.models.built_form.building_use_definition import BuildingUseDefinition +from footprint.main.models.built_form.placetype import Placetype + +from footprint.main.models.keys.keys import Keys +from footprint.main.utils.utils import get_or_none + +__author__ = 'calthorpe_associates' + +#def construct_built_forms(): +# buildtype_data = construct_buildingtypes() +# return merge(buildtype_data, construct_placetypes(buildtype_data['buildingtypes'])) + +# import the logging library +import logging +# Get an instance of a logger +logger = logging.getLogger(__name__) + +def load_buildings_csv(): + """ + :return: ImportedBuilding objects imported from UrbanFootprint v0.1 Built Form default set, csv + """ + # Load building attribute data from a csv and used it to create Building instances + dir = os.path.dirname(__file__) + imported_buildings = ImportedBuilding.import_from_filename('{0}/buildings_with_subcategories.csv'.format(dir)) + + # this is a fix, because the top level categories in the source csv are sometimes wrong. we recalculate those top-level + # categories here + + for building in imported_buildings: + building.percent_retail = sum( + [building.percent_retail_services, building.percent_restaurant, building.percent_accommodation, + building.percent_arts_entertainment, building.percent_other_services] + ) + building.percent_industrial = sum( + [building.percent_manufacturing, building.percent_transport_warehouse, building.percent_wholesale, + building.percent_construction_utilities, building.percent_agriculture, building.percent_extraction] + ) + building.percent_residential = sum( + [building.percent_single_family_large_lot, building.percent_single_family_small_lot, + building.percent_attached_single_family, building.percent_multifamily_2_to_4, + building.percent_multifamily_5_plus] + ) + building.percent_office = sum( + [building.percent_office_services, building.percent_education_services, building.percent_medical_services, + building.percent_public_admin] + ) + + return imported_buildings + +def load_buildingtype_csv(): + """ + :return: ImportedBuildingtype objects imported from UrbanFootprint v0.1 Built Form default set, csv + """ + dir = os.path.dirname(__file__) + imported_buildingtypes = ImportedBuildingtype.import_from_filename('{0}/buildingtypes.csv'.format(dir)) + return imported_buildingtypes + +def load_placetype_csv(): + """ + :return: ImportedPlacetype objects imported from UrbanFootprint v0.1 Built Form default set, csv + """ + dir = os.path.dirname(__file__) + imported_placetypes = ImportedPlacetypes.import_from_filename('{0}/placetypes.csv'.format(dir)) + return imported_placetypes + +# By default we create a BuiltFormSet for each class, one for Placetypes, BuildingTypes, and Buildings. +# Sets can contain instances of multiple classes, but this is the configuration is easiest to understand +def built_form_sets(): + # Configuration for BuiltFormSets + return [ + dict( + key='placetype', + name='Placetypes', + description='BuiltFormSet containing only placetypes', + clazz=Placetype + ), + dict( + key='buildingtype', + name='Buildingtypes', + description='BuiltFormSet containing only buildingtypes', + clazz=PlacetypeComponent + ), + dict( + key='building', + name='Buildings', + description='BuiltFormSet containing only buildings', + clazz=PrimaryComponent + ) + ] + +def construct_buildings(): + """ + :return: Building objects (UrbanFootprint v0.1 Built Form default set) + """ + buildings = [] + + for import_building in load_buildings_csv(): + + building_attributes = dict( + name=import_building.name, + floors=import_building.floors, + total_far=import_building.total_far, + softscape_and_landscape_percent=import_building.softscape_and_landscape_percent, + pervious_hardscape_percent=import_building.pervious_hardscape_percent, + impervious_hardscape_percent=import_building.impervious_hardscape_percent, + impervious_roof_percent=import_building.impervious_roof_percent, + parking_structure_square_feet=import_building.parking_structure_square_feet, + parking_spaces=import_building.parking_spaces, + residential_average_lot_size=import_building.residential_average_lot_size, + irrigated_percent=import_building.irrigated_percent, + ) + + building = dict(building_attributes=building_attributes) + + buildings.append(building) + + return buildings + + +def construct_buildingtypes(): + """ + :return: BuildingType objects (UrbanFootprint v0.1 Built Form default set) + """ + buildingtype_names = [] + buildingtypes = [] + + for b in load_buildings_csv(): + buildingtype_names.append(b.building_type) + + for buildingtype_name in set(buildingtype_names): + building_attributes = dict(name=buildingtype_name) + buildingtypes.append(dict(building_attributes=building_attributes)) + + return buildingtypes + + +def construct_placetypes(): + """ + :return: PlaceType objects (UrbanFootprint v0.1 Built Form default set) + """ + placetypes = [] + for placetype in load_placetype_csv(): + building_attributes = dict(name=placetype.name) + placetype = dict(building_attributes=building_attributes, color=placetype.color) + placetypes.append(placetype) + return placetypes + + +def construct_building_percents(buildingtypes, buildings): + """ + :return: BuildingPercent objects (UrbanFootprint v0.1 Built Form default set) + """ + building_percents = [] + for import_building in load_buildings_csv(): + building_percent = dict( + building_name=import_building.name, + building=buildings[import_building.name], + buildingtype_name=import_building.building_type, + buildingtype=buildingtypes[import_building.building_type], + percent=import_building.percent_of_building_type + ) + building_percents.append(building_percent) + return building_percents + + +def construct_building_use_percents(buildings): + """ + :return: BuildingUsePercent objects (UrbanFootprint v0.1 Built Form default set) + """ + building_use_percents = [] + + def make_building_use_percent_dict(import_building, building_use, building_use_category): + if building_use == 'Armed Forces': + return None + import_use_field = building_use.lower().replace(' ', '_') + import_use_category_field = building_use_category.lower().replace(' ', '_') + use_percent = getattr(import_building, 'percent_{0}'.format(import_use_field)) + if use_percent > 0: + + try: + efficiency = getattr(import_building, '{0}_efficiency'.format(import_use_category_field)) + square_feet_per_unit = getattr(import_building, '{0}_square_feet_per_unit'.format(import_use_category_field)) + + except: + efficiency = 1 + square_feet_per_unit = 10000 + + building_use_definition, created, updated = BuildingUseDefinition.objects.update_or_create(name=building_use) + + if not building_use_definition: + return None + + building_uses = dict( + building_use_definition=BuildingUseDefinition.objects.get(name=building_use), + percent=use_percent, + efficiency=efficiency, + square_feet_per_unit=square_feet_per_unit, + ) + + building_use_percent = dict( + built_form_dict=building, + built_form_name=building['building_attributes']['name'], + built_form_uses=building_uses + ) + + return building_use_percent + + for import_building in load_buildings_csv(): + if import_building.name not in buildings: + continue + category_sums = defaultdict(float) + building = buildings[import_building.name] + for building_use, building_use_category in Keys.BUILDING_USE_DEFINITION_CATEGORIES.items(): + building_use_percent = make_building_use_percent_dict(import_building, building_use, building_use_category) + if building_use_percent: + category_dict = category_sums[building_use_category] = defaultdict(float) + building_use_percents.append(building_use_percent) + category_dict['percent'] += building_use_percent['built_form_uses']['percent'] + del building_use_percent + + return building_use_percents + + +def construct_placetype_component_percents(placetype_dict, buildingtype_dict): + """ + :return: + """ + + input_buildingtypes = load_buildingtype_csv() + + buildingtypes = [] + for buildingtype in input_buildingtypes: + if buildingtype.gross_net_flag == 'Gross': + buildingtypes.append(buildingtype) + + input_placetypes = load_placetype_csv() + input_placetype_dict = map_to_dict(lambda input_placetype: [input_placetype.name, input_placetype], input_placetypes) + placetype_component_percents = [] + placetype_component_dict = dict() + + for name, placetype in placetype_dict.items(): + + for input_buildingtype in buildingtypes: + + placetype_name = placetype['building_attributes']['name'].strip() + + placetype_component_percent = getattr(input_buildingtype, input_placetype_dict[placetype_name].clean_name) + placetype_component_name = input_buildingtype.name.strip() + + default_placetype_component_dict = {'buildingtypes': {}, 'infrastructuretypes': {}, } + + placetype_component_dict[placetype_name] = placetype_component_dict.get(placetype_name, + default_placetype_component_dict) + + if placetype_component_percent > 0: + if placetype_component_name.strip() in Keys.INFRASTRUCTURE_TYPES: + placetype_component_dict[placetype_name]['infrastructuretypes'][placetype_component_name] = { + 'percent': placetype_component_percent, + } + else: + category = get_or_none(PlacetypeComponentCategory, name=input_buildingtype.category.strip) + if category: + placetype_component_dict[placetype_name]['buildingtypes'][placetype_component_name] = { + 'category': category, + 'percent': placetype_component_percent, + } + + return placetype_component_dict + +def construct_built_forms(): + """ + Calls all the functions necessary to generate the Built Forms to mimic the + starter set of v0.1 UrbanFootprint Built Forms + """ + buildings = construct_buildings() + buildingtypes = construct_buildingtypes() + placetypes = construct_placetypes() + building_dict = map_to_dict(lambda building: [building['building_attributes']['name'], building], buildings) + buildingtype_dict = map_to_dict(lambda buildingtype: [buildingtype['building_attributes']['name'], buildingtype], buildingtypes) + + return {'placetypes': placetypes, + 'buildingtypes': buildingtypes, + 'buildings': buildings, + 'building_percents': construct_building_percents(buildingtype_dict, building_dict), + 'building_use_percents': construct_building_use_percents(building_dict), + 'placetype_component_percents': construct_placetype_component_percents( + map_to_dict(lambda placetype: [placetype['building_attributes']['name'], placetype], placetypes), + buildingtype_dict)} + + +def construct_sample_built_forms(): + """ + Builds a sample set of built forms for testing + """ + placetypes = construct_sample_placetypes() + buildingtypes = construct_sample_buildingtypes(placetypes) + + building_percents = construct_sample_building_percents(buildingtypes) + + buildings = [building['building'] for building in building_percents] + building_dict = map_to_dict(lambda building: [building['building_attributes']['name'], building], buildings) + buildingtype_dict = map_to_dict(lambda buildingtype: [buildingtype['building_attributes']['name'], buildingtype], buildingtypes) + + return {'placetypes': placetypes, + 'buildingtypes': buildingtypes, + 'buildings': buildings, + 'building_percents': building_percents, + 'building_use_percents': construct_building_use_percents(building_dict), + 'placetype_component_percents': construct_placetype_component_percents( + map_to_dict(lambda placetype: [placetype['building_attributes']['name'], placetype], placetypes), + buildingtype_dict)} + + +def construct_sample_placetypes(): + """ + :return: a sample set of four placetypes + """ + sample_placetypes = list(construct_placetypes()[i] for i in [1, 2, 3, 8, 9, 10, 16, 20, 25, 29, 30]) + + return sample_placetypes + + +def construct_sample_buildingtypes(sample_placetypes): + sample_buildingtypes = [] + all_buildingtypes = construct_buildingtypes() + buildingtype_dict = map_to_dict( + lambda buildingtype: [buildingtype['building_attributes']['name'], buildingtype], all_buildingtypes + ) + + sample_buildingtype_percents = construct_placetype_component_percents( + map_to_dict(lambda placetype: [placetype['building_attributes']['name'], placetype], sample_placetypes), + buildingtype_dict) + + buildingtypes = [] + for placetype, components in sample_buildingtype_percents.items(): + for buildingtype, attributes in components['buildingtypes'].items(): + buildingtypes.append(buildingtype) + + for buildingtype in set(buildingtypes): + sample_buildingtypes.append({'building_attributes': {'name': buildingtype}}) + + return sample_buildingtypes + + +def construct_sample_building_percents(sample_buildingtypes): + all_buildings = construct_buildings() + building_dict = map_to_dict(lambda building: [building['building_attributes']['name'], building], all_buildings) + + sample_buildingtype_dict = map_to_dict(lambda buildingtype: [buildingtype['building_attributes']['name'], buildingtype], sample_buildingtypes) + sample_building_percents = [] + + for import_building in load_buildings_csv(): + if import_building.building_type not in sample_buildingtype_dict: + print "BuildingType " + import_building.building_type + " is not used in this set :: skipping" + continue + + buildingtype = sample_buildingtype_dict[import_building.building_type] + building_percent = dict( + building_name=import_building.name, + building=building_dict[import_building.name], + buildingtype_name=import_building.building_type, + buildingtype=buildingtype, + percent=import_building.percent_of_building_type + ) + sample_building_percents.append(building_percent) + + + return sample_building_percents \ No newline at end of file diff --git a/footprint/main/tests/test_data/sample_config_entities.py b/footprint/main/tests/test_data/sample_config_entities.py new file mode 100644 index 000000000..5edeeb7b2 --- /dev/null +++ b/footprint/main/tests/test_data/sample_config_entities.py @@ -0,0 +1,116 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.geos import MultiPolygon, Polygon +from footprint.main.mixins.category import Category +from django.conf import settings +__author__ = 'calthorpe_associates' + +regions = [ + { + 'key': 'eastbaywest', + 'name': 'East Bay West', + 'description': 'Alameda is possibly the best county in the Bay Area. It contains sweeping vistas, intellectual firepower, and hip culture', + 'bounds': MultiPolygon([Polygon( + ((-122.719, 37.394), # bottom left + (-122.719, 38.059), # top left + (-121.603, 38.059), # top right + (-121.603, 37.394), # bottom right + (-122.719, 37.394), # bottom left + ) )]) + }, + { + 'key': 'eastbayeast', + 'name': 'East Bay East', + 'description': "Contra Costa county is so named for its juxtaposition with San Francisco", + 'bounds': MultiPolygon([Polygon( + ((-122.4319, 37.7182), # bottom left + (-122.4319, 38.1002), # top left + (-121.5356, 38.1002), # top right + (-121.5356, 37.7182), # bottom right + (-122.4319, 37.7182), # bottom left + ) )]) + } +] + +projects = [ + { + 'key': 'alameda', + 'name': 'Alameda County', + 'description': 'This project is aimed at making Alameda county event cooler', + 'base_table': settings.STATIC_ROOT + 'sample_data/eastbaywest_alameda_base', + + 'base_year': 2012, + 'region_index': 0, + 'bounds': MultiPolygon([Polygon( + ((-122.719, 37.394), # bottom left + (-122.719, 38.059), # top left + (-121.603, 38.059), # top right + (-121.603, 37.394), # bottom right + (-122.719, 37.394), # bottom left + ) )]) + }, + { + 'key': 'contracosta', + 'name': 'Contra Costa County', + 'description': "This project intents to explore densification options around the county's train stations", + 'base_year': 2012, + 'region_index': 1, + 'bounds': MultiPolygon([Polygon( + ((-122.4319, 37.7182), # bottom left + (-122.4319, 38.1002), # top left + (-121.5356, 38.1002), # top right + (-121.5356, 37.7182), # bottom right + (-122.4319, 37.7182), # bottom left + ) )]) + } +] + +scenarios = [ + { + 'key': 'smart', + 'name': 'Smart Growth Scenario', + 'description': 'This scenario is aimed at making Alameda county grow up smart', + 'year': 2030, + 'project_index': 0, + 'categories': [Category(key='category', value='smart'), Category(key='coastal', value='yes')] + }, + { + 'key': 'trend', + 'name': 'Trend Scenario', + 'description': "This business as usual scenario is Alameda county's road to mediocrity", + 'year': 2030, + 'project_index': 0, + 'categories': [Category(key='category', value='dumb'), Category(key='coastal', value='yes')] + }, + { + 'key': 'smart_cc', + 'name': 'Smart Growth Scenario', + 'description': 'This scenario is aimed at making Contra Costa county grow up smart', + 'year': 2030, + 'project_index': 1, + 'categories': [Category(key='category', value='smart'), Category(key='coastal', value='no')] + }, + { + 'key': 'trend_cc', + 'name': 'Trend Scenario', + 'description': "This business as usual scenario is Contra Costa's road to mediocrity", + 'year': 2030, + 'project_index': 1, + 'categories': [Category(key='category', value='dumb'), Category(key='coastal', value='no')] + } +] diff --git a/footprint/main/tests/test_data/sample_config_entities_sacog.py b/footprint/main/tests/test_data/sample_config_entities_sacog.py new file mode 100644 index 000000000..6f7a63c86 --- /dev/null +++ b/footprint/main/tests/test_data/sample_config_entities_sacog.py @@ -0,0 +1,124 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.contrib.gis.geos import MultiPolygon, Polygon +from footprint.main.mixins.category import Category + +__author__ = 'calthorpe_associates' + +regions = [ + { + 'key': 'sacog', + 'name': 'SACOG', + 'description': 'The SACOG region', + 'bounds': MultiPolygon([Polygon( + ((-122.719, 37.394), # bottom left + (-122.719, 38.059), # top left + (-121.603, 38.059), # top right + (-121.603, 37.394), # bottom right + (-122.719, 37.394), # bottom leftsample_config_entities + ) )]) + }, +] + +projects = [ + { + 'key': 'saccity', + 'name': 'Sacramento City', + 'description': 'Sample project of the city of Sacramento', + 'base_year': 2013, + 'region_index':0, + 'bounds': MultiPolygon([Polygon( + ((-122.719, 37.394), # bottom left + (-122.719, 38.059), # top left + (-121.603, 38.059), # top right + (-121.603, 37.394), # bottom right + (-122.719, 37.394), # bottom left + ) )]) + }, + { + 'key': 'saccounty', + 'name': 'Sacramento County', + 'description': "Sample project of the county of Sacramento", + 'base_year': 2013, + 'region_index':0, + 'bounds': MultiPolygon([Polygon( + ((-122.4319, 37.7182), # bottom left + (-122.4319, 38.1002), # top left + (-121.5356, 38.1002), # top right + (-121.5356, 37.7182), # bottom right + (-122.4319, 37.7182), # bottom left + ) )]) + } +] + +scenarios = [ + { + 'key': 'smartcity', + 'name': 'Smart Growth Scenario', + 'description': 'Aggressive smart-growth scenario for city', + 'year': 2030, + 'project_index': 0, + 'selections': dict(built_form_sets='placetype'), + 'categories': [Category(key='category', value='smart'), Category(key='coastal', value='yes')] + }, + { + 'key': 'trendcity', + 'name': 'Trend Scenario', + 'description': 'Business-as-usual scenario for city', + 'year': 2030, + 'project_index': 0, + 'selections': dict(built_form_sets='placetype'), + 'categories': [Category(key='category', value='trend'), Category(key='coastal', value='yes')] + }, + { + 'key': 'greencity', + 'name': 'Green Scenario', + 'description': 'Green scenario for city', + 'year': 2030, + 'project_index': 0, + 'selections': dict(built_form_sets='placetype'), + 'categories': [Category(key='category', value='smart'), Category(key='coastal', value='yes')] + }, + { + 'key': 'city', + 'name': 'Growth Scenario', + 'description': 'Business-as-usual growth scenario for city', + 'year': 2030, + 'project_index': 0, + 'selections': dict(built_form_sets='placetype'), + 'categories': [Category(key='category', value='trend'), Category(key='coastal', value='yes')] + }, + { + 'key': 'smartcounty', + 'name': 'Smart Growth Scenario', + 'description': 'Aggressive smart-growth scenario for county', + 'year': 2030, + 'project_index': 1, + 'selections': dict(built_form_sets='placetype'), + 'categories': [Category(key='category', value='smart'), Category(key='coastal', value='no')] + }, + { + 'key': 'trendcounty', + 'name': 'Trend Scenario', + 'description': 'Business-as-usual scenario fo city', + 'year': 2030, + 'project_index': 1, + 'selections': dict(built_form_sets='placetype'), + 'categories': [Category(key='category', value='trend'), Category(key='coastal', value='no')] + } +] diff --git a/footprint/main/tests/test_data/sample_media.py b/footprint/main/tests/test_data/sample_media.py new file mode 100644 index 000000000..59f11ea2f --- /dev/null +++ b/footprint/main/tests/test_data/sample_media.py @@ -0,0 +1,40 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + +media = [ + { + 'key': 'logo', + 'name': 'The logo for this product', + 'description': 'The logo can be featured somewhere prominent', + 'url': 'http://logosthatmatter.lock', + 'content_type': Keys.CONTENT_TYPE_PNG, + 'content':'gobblygook' + }, + { + 'key': 'audio_clip', + 'name': 'The perfect sound for this product', + 'description': 'This audio clip should be played immediately', + 'url': 'http://audiofromparadise.net', + 'content_type': 'mp3', + 'content':'noisygobblygook' + }, + ] + diff --git a/footprint/main/tests/test_data/sample_policy_sets.py b/footprint/main/tests/test_data/sample_policy_sets.py new file mode 100644 index 000000000..bf18b9f63 --- /dev/null +++ b/footprint/main/tests/test_data/sample_policy_sets.py @@ -0,0 +1,58 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.utils.range import Range + +__author__ = 'calthorpe_associates' + +policy_sets = [ + { + 'name':'Old Standard', + 'key':'oldstandard', + 'description':'The oldest and most trusted policy set', + 'policies': + [ + { 'key':'transit', 'name':'Transit', 'policies': + [{'key':'transit_speed', 'name':'Transit Speed', 'values':['fast','leisurely','sluggish']}, + {'key':'transit_dwell', 'name':'Transit Dwell', 'values':['low','moderate','forever']}, + {'key':'transit_abundance', 'name':'Transit Abundance', 'values':['high','medium','dismal']}] + }, + { 'key':'health', 'name':'Health', 'policies': + [ {'key':'diseases', 'name':'Diseases', 'values':['elephantitus','leperacy','hotdog finger']}, + {'key':'decibels', 'name':'Decibels', 'values':Range(0,1000000)}] + } + ] + }, + { + 'name':'Fast and/or Furious', + 'key':'fastfurious', + 'description':'A little more radical policy set', + 'policies': + [ + { 'key':'transit', 'name':'Transit', 'policies': + [{'key':'transit_speed', 'name':'Transit Speed', 'values':['fast','leisurely','sluggish']}, + {'key':'transit_dwell', 'name':'Transit Dwell', 'values':['low','moderate','forever']}, + {'key':'transit_abundance', 'name':'Transit Abundance', 'values':['high','medium','dismal']}] + }, + { 'key':'energy', 'name':'Energy', 'policies': + [ {'key':'source', 'name':'Energy Source', 'values':['treadmills', 'atomic', 'solar']}, + {'key':'potential', 'name':'Energy Potential', 'values':Range(0,1)}] + } + ] + } +] + diff --git a/footprint/main/tests/test_management/__init__.py b/footprint/main/tests/test_management/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_management/test_footprint_init.py b/footprint/main/tests/test_management/test_footprint_init.py new file mode 100644 index 000000000..06b0f513d --- /dev/null +++ b/footprint/main/tests/test_management/test_footprint_init.py @@ -0,0 +1,12 @@ +from django.core.management import call_command +from footprint.main.initialization.data_provider import DataProvider +from footprint.main.models import Scenario + +__author__ = 'calthorpe_associates' + +import unittest + +class TestFootprintInit(unittest.TestCase): + def test_import(self): + call_command('footprint_init') + assert(Scenario.objects.count(), DataProvider().scenarios()) diff --git a/footprint/main/tests/test_management/test_user.py b/footprint/main/tests/test_management/test_user.py new file mode 100644 index 000000000..489c9c878 --- /dev/null +++ b/footprint/main/tests/test_management/test_user.py @@ -0,0 +1,14 @@ +from django.utils import unittest +from footprint.main.initialization.data_provider import DataProvider +from nose.tools import assert_equal + +__author__ = 'calthorpe_associates' + +class TestUser(unittest.TestCase): + def test_add_user(self): + results = DataProvider().user('testy', 'testy', 'test@test.test', 'TEST_API_KEY') + user = results['user'] + api_key = results['api_key'] + assert_equal(user.username, 'testy') + assert_equal(user.email, 'test@test.test') + assert_equal(api_key.key, 'TEST_API_KEY') diff --git a/footprint/main/tests/test_models/__init__.py b/footprint/main/tests/test_models/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_models/test_analysis_module/__init__.py b/footprint/main/tests/test_models/test_analysis_module/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_models/test_analysis_module/test_core.py b/footprint/main/tests/test_models/test_analysis_module/test_core.py new file mode 100644 index 000000000..380267023 --- /dev/null +++ b/footprint/main/tests/test_models/test_analysis_module/test_core.py @@ -0,0 +1,77 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from datetime import datetime +import random +from time import sleep +from nose import with_setup +from footprint.main.initialization.data_provider import DataProvider +from footprint.main.models.analysis_module.core_module.core import Core +from footprint.main.models.application_initialization import application_initialization +from footprint.main.models.keys.keys import Keys +from footprint.main.tests.test_models.test_config_entity.test_config_entity import TestConfigEntity + +__author__ = 'calthorpe_associates' + + +class TestCore(TestConfigEntity): + def setup(self): + super(TestCore, self).__init__() + application_initialization() + + def teardown(self): + super(TestCore, self).__init__() + + @with_setup(setup, teardown) + def test_scenario_core(self): + """ + Tests scenario creation + :return: + """ + scenarios = DataProvider().scenarios() + scenario = scenarios[0] + scenario_built_form_feature_manager = scenario.feature_class_of_db_entity_key('scenario_built_form_layer').objects + built_form_set = scenario.selected_built_form_set() + built_form_ids = map(lambda built_form: built_form.id, built_form_set.built_form_definitions.all()) + + length = scenario_built_form_feature_manager.count() + assert (length > 0) + # Dirty up the features + for scenario_built_form_feature_manager in scenario_built_form_feature_manager.all(): + scenario_built_form_feature_manager.built_form_id = random.choice(built_form_ids) + scenario_built_form_feature_manager.dirty = True + scenario_built_form_feature_manager.save() + + core = Core.objects.get(config_entity=scenario) + timestamp = datetime.now() + core.start() + sleep(3) + + # Make sure we have values for all the analysis table classes + for db_entity_key in [Keys.DB_ABSTRACT_GROSS_INCREMENT_FEATURE, Keys.DB_ABSTRACT_INCREMENT_FEATURE, + Keys.DB_ABSTRACT_END_STATE_FEATURE]: + db_entity = scenario.selected_db_entity(db_entity_key) + FeatureClass = scenario.feature_class_of_db_entity_key(db_entity_key) + # Assert that the correct number of rows exist + assert (FeatureClass.objects.count() == length) + # Assert that all rows were updated + assert (len(FeatureClass.objects.filter(updated__gte=timestamp)) == length, + "For table {0}.{1}, not all rows were updated by the core, rather {2} out of {3}".format( + scenario.schema(), + db_entity.table, + len(FeatureClass.objects.filter(updated__gte=timestamp)), + length)) diff --git a/footprint/main/tests/test_models/test_analysis_module/test_primary_base_update.py b/footprint/main/tests/test_models/test_analysis_module/test_primary_base_update.py new file mode 100644 index 000000000..769df03ac --- /dev/null +++ b/footprint/main/tests/test_models/test_analysis_module/test_primary_base_update.py @@ -0,0 +1,40 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from nose import with_setup +from footprint.main.models.application_initialization import application_initialization +from footprint.main.models.base.primary_base_feature import PrimaryParcelFeature +from footprint.main.tests.test_models.test_config_entity.test_config_entity import TestConfigEntity + +__author__ = 'calthorpe_associates' + +class TestPrimaryBaseUpdate(TestConfigEntity): + + def setup(self): + super(TestPrimaryBaseUpdate, self).__init__() + application_initialization() + + def teardown(self): + super(TestPrimaryBaseUpdate, self).__init__() + + @with_setup(setup, teardown) + def test_primary_base_update(self): + """ + Tests scenario creation + :return: + """ + pass diff --git a/footprint/main/tests/test_models/test_built_form/__init__.py b/footprint/main/tests/test_models/test_built_form/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_models/test_built_form/test_building.py b/footprint/main/tests/test_models/test_built_form/test_building.py new file mode 100644 index 000000000..460b74362 --- /dev/null +++ b/footprint/main/tests/test_models/test_built_form/test_building.py @@ -0,0 +1,43 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +import unittest +from nose import with_setup +from models import Building +from footprint.main.models.config.global_config import global_config_singleton + +__author__ = 'calthorpe_associates' + + +class TestBuilding(unittest.TestCase): + def setup(self): + pass + + def teardown(self): + pass + + @with_setup(setup, teardown) + def test_building_uses(self): + global_config = global_config_singleton() + built_form_set = global_config.built_form_sets.all()[0] + buildings = filter(lambda built_form: isinstance(built_form, Building), + built_form_set.built_form_definitions.all().select_subclasses("building")) + + buildings_with_building_uses = filter(lambda building: len(building.building_uses.all()) > 0, buildings) + assert len(buildings_with_building_uses) > 0 + diff --git a/footprint/main/tests/test_models/test_built_form/test_built_form.py b/footprint/main/tests/test_models/test_built_form/test_built_form.py new file mode 100644 index 000000000..990f3fa06 --- /dev/null +++ b/footprint/main/tests/test_models/test_built_form/test_built_form.py @@ -0,0 +1,244 @@ +from footprint.main.models.built_form.building_attribute_set import BuildingAttributeSet + +__author__ = 'calthorpe_associates' +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.utils.test_utils import reasonably_close +import unittest +from itertools import chain +from django.db.models import Sum +from footprint.main.models.built_form.flat_built_forms import refresh_all_flat_built_forms +from footprint.main.models.keys.keys import Keys + +from footprint.main.models.config.global_config import global_config_singleton +from footprint.main.models.application_initialization import application_initialization + +from footprint.main.models.built_form.placetype import Placetype +from footprint.main.models.built_form.buildingtype import BuildingType +from footprint.main.models.built_form.building import Building +from footprint.main.models.built_form.infrastructure_type import InfrastructureType + +from footprint.main.tests.test_data import sample_built_form_sets + +from footprint.main.initialization.data_provider import DataProvider + +class TestBuiltForm(unittest.TestCase): + + def setup(self): + pass + + def teardown(self): + pass + + def test_import_from_fixtures(self): + DataProvider().built_form_sets() + print "OKAY" + + def test_building_imports(self): + def sum_import_building_subcategories(building): + return sum( + [building.percent_single_family_large_lot, building.percent_single_family_small_lot, + building.percent_attached_single_family, building.percent_multifamily_2_to_4, + building.percent_multifamily_5_plus, + + # Pct_Emp_Office_Svc,Pct_Educ_Svc,Pct_Medical_Svc,Pct_Public_Admin, + building.percent_office_services, building.percent_education_services, + building.percent_medical_services, building.percent_public_admin, + + #Pct_Retail_Svc,Pct_Restuarant,Pct_Accommodation,Pct_Arts_Entertainment,Pct_Other_Svc, + building.percent_retail_services, building.percent_restaurant, building.percent_accommodation, + building.percent_arts_entertainment, building.percent_other_services, + + # Pct_Manufacturing,Pct_Transport_warehouse,Pct_Wholesale,Pct_Construction_Util,Pct_Agriculture, + # Pct_Extraction, + building.percent_manufacturing, building.percent_transport_warehouse, building.percent_wholesale, + building.percent_construction_utilities, building.percent_agriculture, building.percent_extraction] + ) + + def sum_building_industrial_uses(building): + return sum([building.percent_manufacturing, building.percent_transport_warehouse, building.percent_wholesale, + building.percent_construction_utilities, building.percent_agriculture, building.percent_extraction]) + + def sum_building_retail_uses(building): + return sum([building.percent_retail_services, building.percent_restaurant, building.percent_accommodation, + building.percent_arts_entertainment, building.percent_other_services]) + + def sum_import_building_categories(building): + return sum( + [building.percent_industrial, building.percent_retail, building.percent_office, + building.percent_residential] + ) + + def sum_building_residential_uses(building): + return sum( + [building.percent_single_family_large_lot, building.percent_single_family_small_lot, + building.percent_attached_single_family, building.percent_multifamily_2_to_4, + building.percent_multifamily_5_plus] + ) + + def sum_building_office_uses(building): + return sum( + [building.percent_office_services, building.percent_education_services, + building.percent_medical_services, building.percent_public_admin]) + + for building in sample_built_form_sets.load_buildings_csv(): + assert reasonably_close(sum_building_office_uses(building), building.percent_office) + assert reasonably_close(sum_building_residential_uses(building), building.percent_residential) + assert reasonably_close(sum_building_retail_uses(building), building.percent_retail) + assert reasonably_close(sum_building_industrial_uses(building), building.percent_industrial) + + assert building.percent_of_building_type >= 0,"{0} has a percent less than zero !".format(building) + assert .95 < sum_import_building_subcategories(building) < 1.05, \ + "{0}, {1}".format(building.name,sum_import_building_subcategories(building)) + assert .95 < sum_import_building_categories(building) < 1.05, \ + "{0}, {1}".format(building.name,sum_import_building_categories(building)) + + if sum_building_office_uses(building) > 0: + assert building.office_efficiency > 0, "{0}, {1}".format(building.name,building.office_efficiency) + assert building.office_square_feet_per_unit > 0, "{0}, {1}".format(building.name.building.office_square_feet_per_unit) + + if sum_building_residential_uses(building) > 0: + assert building.residential_square_feet_per_unit > 0, "{0}, {1}".format(building.name.building.square_feet_per_unit) + assert building.residential_efficiency > 0, "{0}, {1}".format(building.name.building.residential_efficiency) + if sum([building.percent_single_family_large_lot, building.percent_single_family_small_lot]) > 0: + assert building.residential_average_lot_size > 0, "{0}, {1}".format(building.name.building.residential_average_lot_size) + return True + + def test_built_form_exports(self): + refresh_all_flat_built_forms() + + def test_unit_density(self): + application_initialization() + for built_form in BuildingAttributeSet.objects.all(): + built_form.calculate_combined_pop_emp_density() + built_form.save() + + def test_built_form(self): + application_initialization() + + assert self.test_building_imports() is True + + global_config = global_config_singleton() + built_form_set = global_config.built_form_sets.all() + + test_buildings = Building.objects.all() + test_buildingtypes = BuildingType.objects.all() + test_placetypes = Placetype.objects.all() + test_infrastructuretypes = InfrastructureType.objects.all() + + assert test_buildings.exists() is True + assert test_buildingtypes.exists() is True + assert test_placetypes.exists() is True + assert test_infrastructuretypes.exists() is True + + for built_form in chain(test_buildings, test_buildingtypes, test_placetypes): + # built_form.aggregate_built_form_attributes() + # built_form.building_attributes.calculate_derived_fields() + for use in built_form.building_attributes.buildingusepercent_set.all(): + assert use.efficiency > 0, "{0} {1}".format(built_form, use.building_use_definition.name) + assert use.percent > 0, "{0} {1}".format(built_form, use.building_use_definition.name) + + # all built forms that are aggregates of other built forms must have components that add to 100% + for aggregate_built_form in chain(test_buildingtypes, test_placetypes): + component_percent_field = aggregate_built_form.get_component_field().through.__name__ + component_percents = getattr(aggregate_built_form, "{0}_set".format(component_percent_field.lower())).all() + components_percents_sum = component_percents.aggregate(Sum('percent'))['percent__sum'] + + assert .95 < components_percents_sum < 1.05, \ + 'BAD THING: components of {0} add up to {1} %'.format(aggregate_built_form.name, components_percents_sum*100) + if components_percents_sum != 1: + print aggregate_built_form.name, 'component_error ', components_percents_sum*100 + + assert Building.objects.filter(building_attributes__parking_spaces__gt=0).exists() == True + assert BuildingType.objects.filter(building_attributes__parking_spaces__gt=0).exists() == True + assert Placetype.objects.filter(building_attributes__parking_spaces__gt=0).exists() == True + + assert Building.objects.filter(building_attributes__impervious_hardscape_percent__gt=0).exists() == True + assert BuildingType.objects.filter(building_attributes__impervious_hardscape_percent__gt=0).exists() == True + assert Placetype.objects.filter(building_attributes__impervious_hardscape_percent__gt=0).exists() == True + + assert Building.objects.filter(building_attributes__impervious_roof_percent__gt=0).exists() == True + assert BuildingType.objects.filter(building_attributes__impervious_roof_percent__gt=0).exists() == True + assert Placetype.objects.filter(building_attributes__impervious_roof_percent__gt=0).exists() == True + + # all built forms based on buildings must have building uses that sum to 100% + for built_form in chain(test_buildings, test_buildingtypes, test_placetypes): + for use in built_form.building_attributes.buildingusepercent_set.all(): + assert use.efficiency > 0, "{0} {1}".format(built_form, use.building_use_definition.name) + + assert 1 >= built_form.building_attributes.gross_net_ratio > 0, \ + '{0} has ratio of {1}'.format(built_form.name, built_form.building_attributes.gross_net_ratio) + + assert built_form.building_attributes.total_far > 0, '{0} has floor area ratio of {1}'.format(built_form.name, built_form.building_attributes.total_far) + + building_uses = built_form.building_attributes.buildingusepercent_set.all() + assert building_uses.exists(), built_form + + building_use_categories = building_uses.filter( + building_use_definition__name__in=[ + Keys.RESIDENTIAL_CATEGORY, + Keys.RETAIL_CATEGORY, + Keys.OFFICE_CATEGORY, + Keys.INDUSTRIAL_CATEGORY, + Keys.AGRICULTURAL_CATEGORY, + Keys.MILITARY_CATEGORY] + ) + + building_use_subcategories = building_uses.exclude(id__in=building_use_categories.values_list('id', flat=True)) + + for building_use_categories in [building_use_subcategories, building_use_categories]: + if building_use_categories == building_use_subcategories: + category_type = 'subcategory' + else: + category_type = 'high level category' + + described_uses = building_use_categories.aggregate(Sum('percent'))['percent__sum'] + assert described_uses > 0, '{0} {1} ({2})'.format(built_form.__class__.__name__, built_form.name, category_type) + + described_use_coverage = described_uses / built_form.building_attributes.gross_net_ratio + assert .5 < described_use_coverage < 1.5, 'Built form {0} has uses describing {1} percent (should be 100)'\ + .format(built_form.name, described_use_coverage*100) + if described_use_coverage != 1: + print 'BAD THING: uses of {0} {1} add up to {2} % \n gross/net ratio = {3} \t use = {4} \n \ + '.format(built_form.__class__.__name__, built_form.name, described_use_coverage*100, + built_form.building_attributes.gross_net_ratio, described_uses ) + + assert .97 < described_use_coverage < 1.03, \ + 'BAD THING: uses of {0} add up to {1} % \n gross/net ratio = {2} \n use = {3} \n \ + '.format(built_form.name, described_use_coverage*100, + built_form.building_attributes.gross_net_ratio, described_uses) + + for building_use in building_uses: + assert 1 >= building_use.efficiency > 0, '{0} {1} has efficiency of {2} for {3}'.format( + built_form.__class__.__name__, built_form.name, building_use.efficiency, building_use.building_use_definition.name) + + assert building_use.square_feet_per_unit > 0, '{0} for {1} has {2} square feet per unit'.format( + building_use.building_use_definition.name, built_form.name , building_use.efficiency) + + # with the input fields correctly aggregated, we can now begin deriving the secondary fields + + for use in built_form.building_attributes.buildingusepercent_set.all(): + assert use.unit_density > 0, '{0} {1} has unit density of {2} for {3}'.format( + built_form.__class__.__name__, built_form.name, use.unit_density, use.building_use_definition.name) + assert use.floor_area_ratio > 0, '{0} {1} has far of {2} for {3}'.format( + built_form.__class__.__name__, built_form.name, use.floor_area_ratio, use.building_use_definition.name) + assert use.gross_built_up_area > 0, '{0} {1} has gross built up area of {2} for {3}'.format( + built_form.__class__.__name__, built_form.name, use.gross_built_up_area, use.building_use_definition.name) + assert use.net_built_up_area > 0, '{0} {1} has net built up area of {2} for {3}'.format( + built_form.__class__.__name__, built_form.name, use.net_built_up_area, use.building_use_definition.name) + diff --git a/footprint/main/tests/test_models/test_built_form/test_flat_built_form_exports.py b/footprint/main/tests/test_models/test_built_form/test_flat_built_form_exports.py new file mode 100644 index 000000000..16d4d770f --- /dev/null +++ b/footprint/main/tests/test_models/test_built_form/test_flat_built_form_exports.py @@ -0,0 +1,479 @@ +import logging + +__author__ = 'calthorpe_associates' + +from collections import OrderedDict +import unittest +import itertools +import csv + +from django.db.models import Sum +from nose import with_setup +from django.template.defaultfilters import slugify + +from footprint.main.initialization.built_form.built_form_importer import BuiltFormImporter +# from footprint.main.initialization.fixture import BuiltFormFixture +from footprint.main.mixins.building_aggregate import BuildingAttributeAggregate +from footprint.main.models.config.config_entity import ConfigEntity +from footprint.main.models.application_initialization import initialize_table_definitions, initialize_global_config +from footprint.main.models.built_form.primary_component import PrimaryComponent +from footprint.main.models.built_form.placetype_component import PlacetypeComponent +from footprint.main.models.built_form.placetype import Placetype +from footprint.main.models.built_form.flat_built_forms import refresh_all_flat_built_forms +from footprint.main.models.built_form.flat_built_forms import FlatBuiltForm +from footprint.main.models.config.global_config import global_config_singleton +from footprint.main.models.keys.keys import Keys +from footprint.main.publishing import built_form_publishing +from footprint.main.utils.utils import update_and_return_dict +from django.conf import settings + +logger = logging.getLogger(__name__) +class TestBuiltFormExport(unittest.TestCase): + """ + tests the export of the built forms + """ + + def setup(self): + initialize_table_definitions() + initialize_global_config() + global_config = global_config_singleton() + # built_form_publishing.on_config_entity_post_save_built_form(None, instance=ConfigEntity._subclassed_config_entity(global_config)) + # print "initializing data..." + # initialize_table_definitions() + # self.data_provider = DataProvider() + # print "creating built forms" + # client_built_form = resolve_fixture( + # "built_form", + # "built_form", + # BuiltFormFixture, + # settings.CLIENT) + # self.built_forms_dict = client_built_form.built_forms() + + def teardown(self): + pass + + def test_acres_parcel(self): + self.setup() + refresh_all_flat_built_forms() + for built_form in FlatBuiltForm.objects.all(): + total_acres_parcel = sum([built_form.acres_parcel_employment, + built_form.acres_parcel_residential, + built_form.acres_parcel_mixed_use]) + if total_acres_parcel < .0001: + logger.warn(built_form.built_form_type + '\t:' + built_form.name + ':' + str(total_acres_parcel)) + else: + logger.debug(built_form.built_form_type + '\t:' + built_form.name + ':' + str(total_acres_parcel)) + + def get_buildings_from_buildingtypes(self, buildingtype_names): + test_buildings_dict = dict() + building_percents = self.built_forms_dict['primary_component_percents'] + + test_buildings_dict['primary_component_percents'] = [building_percent for building_percent in building_percents + if building_percent['buildingtype_name'] in buildingtype_names] + test_buildings_dict['primary_components'] = [building_percent['building'] for building_percent in building_percents + if building_percent['buildingtype_name'] in buildingtype_names] + + building_names = [building_percent['building_name'] for building_percent + in test_buildings_dict['primary_component_percents']] + + test_buildings_dict['building_use_percents'] = [use for use in self.built_forms_dict['building_use_percents'] + if use['built_form_name'] in building_names] + return test_buildings_dict + + def get_components_from_placetypes(self, placetype_names): + test_buildingtypes_dict = dict() + + component_percents = test_buildingtypes_dict['placetype_component_percents'] = {} + buildingtype_names = [] + for placetype in placetype_names: + component_percents[placetype] = self.built_forms_dict['placetype_component_percents'][placetype] + buildingtype_names.append([buildingtype for buildingtype, values + in component_percents[placetype]['placetype_components'].items()]) + test_buildingtypes_dict['placetype_components'] = [buildingtype for buildingtype in component_percents] + + buildingtype_names = list(set([item for sublist in buildingtype_names for item in sublist])) + test_buildingtypes_dict['placetype_components'] = [ + buildingtype for buildingtype in self.built_forms_dict['placetype_components'] + if buildingtype['building_attributes']['name'] in buildingtype_names] + test_buildings_dict = self.get_buildings_from_buildingtypes(buildingtype_names) + test_buildingtypes_dict.update(test_buildings_dict) + + return test_buildingtypes_dict + + def test_building_export(self, buildings_array): + buildings_dict = self.built_forms_dict.copy() + drop_values = ["primary_component_percents", "placetype_component_percents", "placetypes", + "sacog_placetypes", "building_percents"] + + building_names = [building['building_attributes']['name'] for building in buildings_array] + for d in drop_values: + buildings_dict[d] = {} + + buildings_dict['building_use_percents'] = [ + use for use in self.built_forms_dict['building_use_percents'] if use['built_form_name'] in building_names + ] + + buildings_dict['primary_components'] = [building for building in buildings_dict['primary_components'] + if building['building_attributes']['name'] in building_names] + + self.data_provider.persist_built_forms(buildings_dict) + self.test_flat_built_form_values() + + def test_buildingtype_export(self, buildingtype_array): + test_buildingtypes_dict = self.built_forms_dict.copy() + + drop_values = "infrastructure_types", "placetype_component_percents", "placetypes", "sacog_placetypes" + for d in drop_values: + test_buildingtypes_dict[d] = {} + + test_buildingtypes_dict['placetype_components'] = buildingtype_array + + buildingtype_names = [buildingtype['building_attributes']['name'] for buildingtype in buildingtype_array] + + buildings_and_components_dict = self.get_buildings_from_buildingtypes(buildingtype_names) + + test_buildingtypes_dict.update(buildings_and_components_dict) + self.data_provider.persist_built_forms(test_buildingtypes_dict) + self.test_flat_built_form_values() + + def test_placetype_export(self, placetype_array): + test_placetype_dict = self.built_forms_dict.copy() + + drop_values = ["sacog_placetypes"] + + for d in drop_values: + test_placetype_dict[d] = {} + test_placetype_dict['placetypes'] = placetype_array + + placetype_names = [placetype['building_attributes']['name'] for placetype in placetype_array] + components_dict = self.get_components_from_placetypes(placetype_names) + test_placetype_dict.update(components_dict) + self.data_provider.persist_built_forms(test_placetype_dict) + self.test_flat_built_form_values() + + def test_one_buildingtype_export(self): + buildingtypes = [self.built_forms_dict['placetype_components'][0]] + self.test_buildingtype_export(buildingtypes) + + def test_one_placetype_export(self): + test_placetypes = [placetype for placetype in self.built_forms_dict['placetypes'] + if placetype.name == 'Office/Industrial'] + self.test_placetype_export(test_placetypes) + + def test_all_placetype_exports(self): + test_placetypes = self.built_forms_dict['placetypes'] + self.test_placetype_export(test_placetypes) + + def reasonably_close(self, value1, value2): + return abs(value1 - value2) < .005 + + def test_one_building_export(self): + test_building = [self.built_forms_dict['primary_components'][0]] + self.test_building_export(test_building) + + def test_all_building_exports(self): + test_buildings = self.built_forms_dict['primary_components'] + self.test_building_export(test_buildings) + + def test_rural_employment_buildings(self): + buildingtype_name = ["Rural Employment"] + buildings = self.get_buildings_from_buildingtypes([buildingtype_name])['primary_components'] + self.test_building_export(buildings) + + def test_non_rural_employment_buildings(self): + buildingtypes = [buildingtype['building_attributes']['name'] for buildingtype in self.built_forms_dict['placetype_components'] + if buildingtype['building_attributes']['name'] != 'Rural Employment'] + buildings = self.get_buildings_from_buildingtypes(buildingtypes)['primary_components'] + self.test_building_export(buildings) + + def test_non_rural_employment_buildingtypes(self): + buildingtypes = [buildingtype for buildingtype in self.built_forms_dict['placetype_components'] + if buildingtype['building_attributes']['name'] != 'Rural Employment'] + self.test_buildingtype_export(buildingtypes) + + def test_non_rural_employment_placetypes(self): + rural_placetype_names = [ + "Low Density Employment Park", + "Rural Ranchettes", + "Rural Employment", + "Institutional", + "Parks and Open Space" + ] + placetypes = [placetype for placetype in self.built_forms_dict['placetypes']] + + + def test_flat_built_form_values(self): + categories = (Keys.OFFICE_CATEGORY, Keys.OFFICE_SUBCATEGORIES), \ + (Keys.AGRICULTURAL_CATEGORY, Keys.AGRICULTURAL_SUBCATEGORIES), \ + (Keys.RETAIL_CATEGORY, Keys.RETAIL_SUBCATEGORIES), \ + (Keys.RESIDENTIAL_CATEGORY, Keys.RESIDENTIAL_SUBCATEGORIES), \ + (Keys.INDUSTRIAL_CATEGORY, Keys.INDUSTRIAL_SUBCATEGORIES) + + for building in PrimaryComponent.objects.all(): + building_uses = building.building_attributes.buildingusepercent_set.all() + # checks that the major use categories have percents that are the sum of their subcategories + for category, subcategory in categories: + subcategories = building_uses.filter(building_use_definition__name__in=subcategory) + if subcategories.exists(): + subcategories_sum = subcategories.aggregate(Sum('percent'))['percent__sum'] + category = building_uses.get(building_use_definition__name=category) + assert self.reasonably_close(category.percent, subcategories_sum) + del category, subcategory, subcategories + del building_uses + + print "exporting built forms to flat table..." + refresh_all_flat_built_forms() + assert FlatBuiltForm.objects.all().exists() + print "checking data output of flat built forms..." + for export_built_form in FlatBuiltForm.objects.all(): + + report_dict = dict( + built_form_name=export_built_form.__dict__['name'], + clazz=export_built_form.built_form_type, + condition="", + operator="!=", + value_1="", + value_2="" + ) + report_string = "*{clazz}* {built_form_name}: {condition} {value_1} {operator} {value_2}".format + + assert self.reasonably_close(export_built_form.residential_density, export_built_form.dwelling_unit_density), \ + report_string(**update_and_return_dict(report_dict, dict( + condition="residential density | unit density", + value_1=export_built_form.residential_density, + value_2=export_built_form.dwelling_unit_density))) + + top_level_categories_density_sum = sum([ + export_built_form.acres_parcel_employment, + export_built_form.acres_parcel_residential, + export_built_form.acres_parcel_mixed_use + ]) + + assert self.reasonably_close(top_level_categories_density_sum, export_built_form.gross_net_ratio), \ + report_string(**update_and_return_dict(report_dict, dict( + value_1=top_level_categories_density_sum, + operator='!=', + value_2=export_built_form.gross_net_ratio + ))) + + for employment_type in ['retail', 'office', 'industrial', 'agriculture']: + if getattr(export_built_form, "acres_parcel_employment_{0}".format(employment_type), None) > 0: + assert getattr(export_built_form, "{0}_density".format(employment_type)) > 0, \ + report_string(**update_and_return_dict(report_dict, dict( + operator="has no {0}_density, but does have acres_parcel_employment_{0}".format( + employment_type) + ))) + print report_string(**update_and_return_dict(report_dict, {'operator': "OK"})) + + @with_setup(setup, teardown) + def test_example_creation(self): + + examples_file = 'placetype_examples.csv' + # # Read in placetype examples and create a dictionary so you + bf_examples_path = '%s/main/sproutcore/apps/fp/resources/Text/%s' % (settings.PROJECT_ROOT, examples_file) + reader = csv.DictReader(open(bf_examples_path, "rU")) + + #This dictionary has builtform id's as the key, and the value is an array of dictionaries, each containing + #data about an example area + bf_examples = {} + + for row in reader: + if row: + pt__key = row["pt__key"] + if bf_examples.get(pt__key): + bf_examples[pt__key].append(row) + else: + bf_examples[pt__key] = [row] + + built_forms_dict = BuiltFormImporter().construct_built_forms('default') + for placetype_dict in built_forms_dict['placetypes']: + name = placetype_dict['building_attributes'].pop('name', None) + + placetype, created, updated = Placetype.objects.update_or_create( + key='pt__' + slugify(name).replace('-', '_'), + defaults=dict( + name=name, + intersection_density=placetype_dict['intersection_density']) + ) + building_attributes_dict = placetype_dict['building_attributes'] + placetype.update_or_create_built_form_examples(bf_examples.get(placetype.key) if bf_examples.get(placetype.key) else []) + + def test_built_form_exports(self): + """ + tests the built forms incrementally, so that problems that start with the building level of abstraction are + identified before running the full suite of built form imports + :return: + """ + + self.setup() + self.test_flat_built_forms() + print "All Built Forms passing tests!" + + def create_use_debug_dict(self, uses): + return {use.building_use_definition.name: OrderedDict( + unit_density=use.unit_density, + floor_area_ratio=use.floor_area_ratio, + vacancy_rate=use.vacancy_rate, + percent=use.percent, + ) + for use in uses} + + def create_component_debug_dict(self, components): + return {component.component().name: OrderedDict( + percent=component.percent, + uses=self.create_use_debug_dict(component.component().building_attributes.buildingusepercent_set.all())) + for component in components} + + def create_debug_dict(self, built_forms_dict): + built_forms = itertools.chain( + built_forms_dict['placetypes'].all(), + built_forms_dict['placetype_components'].all(), + built_forms_dict['primary_components'].all() + ) + + return { + built_form.name: { + 'uses': self.create_use_debug_dict(built_form.building_attributes.buildingusepercent_set.all()), + 'components': self.create_component_debug_dict(built_form.get_all_components()) + if isinstance(built_form, BuildingAttributeAggregate) else None + } for built_form in built_forms + } + + def test_park_and_institutional(self): + self.setup() + placetypes = [placetype for placetype in self.built_forms_dict['placetypes'] + if placetype['building_attributes']['name'] in ['Parks & Open Space']] + self.test_placetype_export(placetypes) + debug_dict = self.create_debug_dict(Placetype.objects.all()) + pass + + def test_rural_residential_types(self): + self.setup() + buildingtypes = [buildingtype for buildingtype in self.built_forms_dict['placetype_components'] + if buildingtype['building_attributes']['name'] in ['Rural Ranchette', 'Rural Residential']] + self.test_buildingtype_export(buildingtypes) + + debug_dict = self.create_debug_dict(PlacetypeComponent.objects.all()) + + pass + + def get_flat_object(self, relational_object): + return FlatBuiltForm.objects.get(built_form_id=relational_object.id) + + def test_flat_component_addition_of_attribute(self, aggregate, component_percents, attr): + + aggregate_attr = getattr(self.get_flat_object(aggregate), attr) + + sum_of_component_attributes = sum([ + getattr(self.get_flat_object(component_percent.component()), attr) * component_percent.percent + for component_percent in component_percents + ]) + + error_dict = { + 'sum_of_components': sum_of_component_attributes, + 'aggregate': aggregate_attr, + 'error': abs(sum_of_component_attributes - aggregate_attr) / sum_of_component_attributes + if sum_of_component_attributes else None, + } + + # assert self.reasonably_close(sum_of_component_attributes, aggregate_attr) + return error_dict + + def test_sum_of_uses(self, built_form): + built_form_uses=list(built_form.building_attributes.buildingusepercent_set.all()) + sum_of_percents = sum([use.percent for use in built_form_uses]) + if not self.reasonably_close(sum_of_percents, 2): + return dict(use_percent_totals=sum_of_percents, + uses=built_form_uses) + return {} + + def test_primary_component_csv(self, client): + error_dict = {} + buildings = BuiltFormImporter().load_buildings_csv(client) + + for building in buildings: + sum_of_uses = sum([ + building.percent_single_family_large_lot, + building.percent_single_family_small_lot, + building.percent_attached_single_family, + building.percent_multifamily_2_to_4, + building.percent_multifamily_5_plus, + + building.percent_office_services, + building.percent_education_services, + building.percent_medical_services, + building.percent_public_admin, + + building.percent_retail_services, + building.percent_restaurant, + building.percent_accommodation, + building.percent_arts_entertainment, + building.percent_other_services, + + building.percent_manufacturing, + building.percent_transport_warehouse, + building.percent_wholesale, + building.percent_construction_utilities, + + building.percent_agriculture, + building.percent_extraction]) + + if not self.reasonably_close(sum_of_uses, 1): + error_dict[building.name] = sum_of_uses + return error_dict + + + + def test_flat_built_forms(self): + + # client_import_errors = self.test_primary_component_csv('sacog') + default_import_errors = self.test_primary_component_csv('default') + self.setup() + refresh_all_flat_built_forms() + error_dict = {'primary_components': {}, 'placetype_components': {}, 'placetypes': {}} + passing_dict = {'primary_components': [], 'placetype_components': [], 'placetypes': []} + + basic_test_attributes = ['residential_density', 'employment_density', 'building_sqft_total'] + + for primary_component in PrimaryComponent.objects.all(): + use_error = self.test_sum_of_uses(primary_component) + + if use_error: + error_dict['primary_components'][primary_component.name] = {'use_error': use_error,} + + placetype_components_error_dict = {} + for placetype_component in PlacetypeComponent.objects.all(): + primary_component_percents = placetype_component.primarycomponentpercent_set.all() + attribute_error_dict = {} + + for test_attr in basic_test_attributes: + + attribute_summing = self.test_flat_component_addition_of_attribute( + placetype_component, primary_component_percents, test_attr) + + if attribute_summing['error'] >= 0.00001: + attribute_error_dict[test_attr] = attribute_summing + + use_error = self.test_sum_of_uses(placetype_component) + if use_error: + attribute_error_dict['use_error'] = use_error + + if attribute_error_dict: + placetype_components_error_dict[placetype_component.name] = attribute_error_dict + elif placetype_component.name not in passing_dict['placetype_components']: + passing_dict['placetype_components'].append(placetype_component.name) + error_dict['placetype_components'] = placetype_components_error_dict + + for placetype in Placetype.objects.all(): + placetype_components = placetype.placetypecomponentpercent_set.all() + + for test_attr in basic_test_attributes: + attribute_summing = self.test_flat_component_addition_of_attribute(placetype, placetype_components, test_attr) + if attribute_summing['error'] > 0.00001: + if placetype.name not in error_dict['placetypes']: + error_dict['placetypes'][placetype.name] = {} + error_dict['placetypes'][placetype.name][test_attr] = attribute_summing + elif placetype.name not in passing_dict['placetypes']: + passing_dict['placetypes'].append(placetype.name) + + return passing_dict, error_dict \ No newline at end of file diff --git a/footprint/main/tests/test_models/test_config_entity/__init__.py b/footprint/main/tests/test_models/test_config_entity/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_models/test_config_entity/test_config_entity.py b/footprint/main/tests/test_models/test_config_entity/test_config_entity.py new file mode 100644 index 000000000..769b13798 --- /dev/null +++ b/footprint/main/tests/test_models/test_config_entity/test_config_entity.py @@ -0,0 +1,50 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from nose.tools import assert_equal +from django.utils.datetime_safe import strftime +from django.utils.timezone import now +from footprint.main.models import Presentation +from footprint.main.models.application_initialization import application_initialization, minimum_initialization +from footprint.main.models.config.scenario import FutureScenario + +__author__ = 'calthorpe_associates' + +from django.utils import unittest + +__author__ = 'calthorpe_associates' + +class TestConfigEntity(unittest.TestCase): + + def setup(self): + pass + + def teardown(self): + pass + + def test_clone(self): + minimum_initialization() + scenario = FutureScenario.objects.all()[0] + key = strftime(now(), '%Y_%m_%d_%H_%M_%S') + future_scenario = FutureScenario( + parent_config_entity=scenario.project, + origin_config_entity=scenario, + year=scenario.year, + name=key, + description='Clone of %s' % scenario.name, + bounds=scenario.bounds, + key=key + ) + future_scenario.save() + assert_equal(len(scenario.computed_built_form_sets()), len(future_scenario.computed_built_form_sets())) + assert_equal(len(scenario.computed_policy_sets()), len(future_scenario.computed_policy_sets())) + assert_equal(len(scenario.computed_db_entity_interests()), len(future_scenario.computed_db_entity_interests())) + assert_equal(len(Presentation.objects.filter(config_entity=scenario)), len(Presentation.objects.filter(config_entity=future_scenario))) diff --git a/footprint/main/tests/test_models/test_config_entity/test_global.py b/footprint/main/tests/test_models/test_config_entity/test_global.py new file mode 100644 index 000000000..1e85c8f63 --- /dev/null +++ b/footprint/main/tests/test_models/test_config_entity/test_global.py @@ -0,0 +1,53 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from nose import with_setup +from footprint.main.models.config.global_config import global_config_singleton +from footprint.main.models.database.information_schema import PGNamespace +from footprint.main.tests.test_models.test_config_entity.test_config_entity import TestConfigEntity + +__author__ = 'calthorpe_associates' + +class TestGlobal(TestConfigEntity): + + def setup(self): + super(TestGlobal, self).__init__() + + def teardown(self): + super(TestGlobal, self).__init__() + + @with_setup(setup, teardown) + def test_application_initialization(self): + global_config = global_config_singleton() + # Assert that the schema is created + assert PGNamespace.objects.schema_exists(global_config.schema()), "The global schema %s does not exist" % global_config.schema() + # Assert that the singleton method returns the single global_config + assert global_config_singleton() == global_config + + @with_setup(setup, teardown) + def test_global_add_policy_set(self): + pass + + @with_setup(setup, teardown) + def test_global_add_policy_set(self): + pass + + @with_setup(setup, teardown) + def test_global_add_db_entity(self): + pass + diff --git a/footprint/main/tests/test_models/test_config_entity/test_project.py b/footprint/main/tests/test_models/test_config_entity/test_project.py new file mode 100644 index 000000000..54619266f --- /dev/null +++ b/footprint/main/tests/test_models/test_config_entity/test_project.py @@ -0,0 +1,59 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.utils import unittest +from nose import with_setup +from footprint.main.models.geospatial.geojson_feature import GeoJsonFeature +from footprint.main.tests.data_provider import DataProvider +from footprint.main.tests.test_models.test_config_entity.test_config_entity import TestConfigEntity +from footprint.main.utils.utils import get_dynamic_model_class + +__author__ = 'calthorpe_associates' + +class TestProject(TestConfigEntity): + + def setup(self): + super(TestProject, self).__init__() + + def teardown(self): + super(TestProject, self).__init__() + + @with_setup(setup, teardown) + def test_project_creation(self): + """ + Tests project creation + :return: + """ + projects = DataProvider().projects() + enhanced_project = projects[0] + # Assert that the enhanced_project has references to the layers + geojson_layers = enhanced_project.computed_db_entities(tags__tag='geojson') + assert(len(geojson_layers)>0) + + for geojson_layer in geojson_layers: + + # Create a subclass to access the layer and a model class + GeoJsonFeatureSubclass = get_dynamic_model_class( + GeoJsonFeature, + '"{0}"."{1}"'.format(geojson_layer.schema, geojson_layer.table), + {}) + + assert len(enhanced_project.db_entities.filter(table=geojson_layer.table))==1 + # Load all the features of the layer that are within the bounds of the enhanced_project. We expect the geojson features to all be within the bounds of the enhanced_project + matched_enhanced_features = GeoJsonFeatureSubclass.objects.filter(geometry__contained=enhanced_project.bounds) + assert len(matched_enhanced_features) > 0 + diff --git a/footprint/main/tests/test_models/test_config_entity/test_region.py b/footprint/main/tests/test_models/test_config_entity/test_region.py new file mode 100644 index 000000000..61150f68e --- /dev/null +++ b/footprint/main/tests/test_models/test_config_entity/test_region.py @@ -0,0 +1,103 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.utils import unittest +from nose import with_setup +from footprint.main.tests.data_provider import DataProvider +from footprint.main.tests.test_models.test_config_entity.test_config_entity import TestConfigEntity +from footprint.main.tests.test_models.test_config_entity.utiltities import SetComparer +from footprint.main.utils.utils import chop_geom +from footprint.main.models.config.global_config import global_config_singleton + +__author__ = 'calthorpe_associates' + +class TestRegion(TestConfigEntity): + + def setup(self): + super(TestRegion, self).__init__() + + def teardown(self): + super(TestRegion, self).__init__() + + @with_setup(setup, teardown) + def test_region_creation(self): + """ + Tests the ability to add regions and their relationship with the global_config + :return: + """ + regions = DataProvider().regions() + reduced_config_entity = regions[1] + parent = global_config_singleton() + + enhanced_config_entity= regions[0] + # Add a DbEntity to the enhanced_config_entity + add_db_entity(enhanced_config_entity) + + # Compare the enhanced_config_entity to its parent to ensure that their count of DbEntities with key='geojson_test' differ. This will also temporary add a DbEntity to the parent to ensure that the child adopts it + SetComparer( + self, + parent, + enhanced_config_entity, + None, + 'db_entities', + add_db_entity).compare_sets(db_entity__key='geojson_test') + + SetComparer(self, parent,enhanced_config_entity, reduced_config_entity, 'policy_sets', add_policy_set).compare_sets() + # Compare the built_form_sets of the two regions to the global_config + SetComparer(self, parent, enhanced_config_entity, reduced_config_entity, 'built_form_sets', add_built_form_set).compare_sets() + + @with_setup(setup, teardown) + def test_subregion_creation(self): + """ + Tests the ability to embed regions with another region + :return: + """ + regions = DataProvider().regions() + enhanced_region = regions[0] + # Create a subregion with an extra policy_set sired by the enhanced_region + enhanced_subregion = DataProvider().generate_region(chop_geom(enhanced_region.bounds), enhanced_region) + enhanced_subregion.add_policy_sets(DataProvider().generate_policy_set()) + enhanced_subregion.save() + # Create a subregion with a removed policy_set sired by the enhanced_region + reduced_subregion = DataProvider().generate_region(chop_geom(enhanced_region.bounds), enhanced_region) + reduced_subregion.remove_policy_sets(reduced_subregion.computed_policy_sets()[0]) + reduced_subregion.save() + # Confirm that manipulating the parent's policy_sets are reflected in the children + SetComparer(enhanced_region, enhanced_subregion, reduced_subregion, 'policy_sets', add_policy_set).compare_sets() + # Now the confirm the relationship to the global_config + assert len(enhanced_subregion.computed_policy_sets()) == len(global_config_singleton().computed_policy_sets()) + 2 + assert len(reduced_subregion.computed_policy_sets()) == len(global_config_singleton().computed_policy_sets()) + SetComparer(global_config_singleton(), enhanced_subregion, reduced_subregion, 'policy_sets', add_policy_set).confirm_set_added_to_parent(2, 0) + +def add_db_entity(config_entity): + """ + This will create the DbEntity instance and the through instance + :param config_entity: + :return: + """ + return DataProvider().create_geojson_layer_and_associate(config_entity)['layer'] + +def add_built_form_set(config_entity): + built_form_set = DataProvider().generate_built_form_set() + config_entity.add_built_form_sets(built_form_set) + return built_form_set + +def add_policy_set(config_entity): + policy_set = DataProvider().generate_policy_set() + config_entity.add_policy_sets(policy_set) + return policy_set + diff --git a/footprint/main/tests/test_models/test_config_entity/test_scenario.py b/footprint/main/tests/test_models/test_config_entity/test_scenario.py new file mode 100644 index 000000000..c66499552 --- /dev/null +++ b/footprint/main/tests/test_models/test_config_entity/test_scenario.py @@ -0,0 +1,60 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.utils import unittest +from nose import with_setup +from footprint.main.models.database.information_schema import InformationSchema +from footprint.main.tests.test_models.test_config_entity.test_config_entity import TestConfigEntity +from footprint.main.utils.utils import parse_schema_and_table + +__author__ = 'calthorpe_associates' + +class TestScenario(TestConfigEntity): + + def setup(self): + super(TestScenario, self).__init__() + + def teardown(self): + super(TestScenario, self).__init__() + + @with_setup(setup, teardown) + def test_scenario_creation(self): + """ + Tests scenario creation + :return: + """ + scenarios = DataProvider().scenarios() + smart_scenario = scenarios[0] + trend_scenario = scenarios[1] + project = smart_scenario.project + + project_geo_json_layers = project.computed_db_entities(tags__tag='geojson') + assert(len(project_geo_json_layers) > 0) + # Test that the trend scenario has the geojson layers of the project + assert(len(trend_scenario.computed_db_entities(tags__tag='geojson')) == len(project_geo_json_layers)) + # Test that the smart scenario has the geojson layers that it registered and those of the project + assert(len(smart_scenario.computed_db_entities(tags__tag='geojson')) > len(project_geo_json_layers)) + + # Verify that the scenarios each have a selected policy_set and built_form_set + for index, scenario in enumerate(scenarios[0:2]): + assert(project.computed_policy_sets()[index]==scenarios[index].selected_policy_set()) + assert(project.computed_built_form_sets()[index]==scenarios[index].selected_built_form_set()) + # Expect all the db_entity_interests to be selected since they have unique keys + # TODO do better testing of key selections + assert(len(scenario.computed_db_entity_interests())==len(scenarios[index].selected_db_entity_interests())) + + self.assertDbEntityTablesAndSubclasses(scenario) diff --git a/footprint/main/tests/test_models/test_config_entity/utiltities.py b/footprint/main/tests/test_models/test_config_entity/utiltities.py new file mode 100644 index 000000000..1c2089ba9 --- /dev/null +++ b/footprint/main/tests/test_models/test_config_entity/utiltities.py @@ -0,0 +1,121 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the # GNU General Public License as published by the Free Software Foundation, version 3 of the License. + +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from django.utils import unittest +from footprint.main.utils.utils import has_explicit_through_class + +__author__ = 'calthorpe_associates' + +class SetComparer(object): + + def __init__(self, test, parent_config_entity, enhanced_config_entity, reduced_config_entity, attribute, set_generator): + """ + Compares set data between a parent ConfigEntity and two children + :param parent_config_entity: The parent ConfigEntity of the two other given entities + :param enhanced_config_entity: A child ConfigEntity with one set added in addition to the parent's + :param reduced_config_entity: A child ConfigEntity with one set removed from the parent's. Set this to None to exclude tests on a reduced instance + :param attribute: 'policy_sets', 'built_form_sets', etc + :param set_generator: a lambda that returns a new set item, which will be added to the set (or used with a through instance) + """ + self.test = test + self.parent_config_entity = parent_config_entity + self.enhanced_config_entity = enhanced_config_entity + self.reduced_config_entity = reduced_config_entity + self.attribute = attribute + self.item_generator = set_generator + # Detect if this Many field has an explicit Through class + self.is_through = has_explicit_through_class(self.parent_config_entity, self.attribute) + + def compare_sets(self, enhanced_difference=1, reduced_difference=1, **kwargs): + """ + This procedure manipulates a parent ConfigEntity and checks the affect on sets of two children. This procedure leaves the instances in their passed in state. + :param enhanced_difference: Defaults to N=1. The enhanced_config_entity should have N more sets than the parent + :param reduced_difference: Defaults to N=1. The reduced_config_entity should have N fewer + :param kwargs: optional query filter to apply to the _computed() method + :return: + """ + + # We expect one more set in the enhanced region than its parent + self.test.assertEqual( + len(self.enhanced_config_entity._computed(self.attribute, **kwargs)), + len(self.parent_config_entity._computed(self.attribute, **kwargs)) + enhanced_difference) + # We expect one less set in the reduced region than its parent + if self.reduced_config_entity: + self.test.assertEqual(len(self.reduced_config_entity._computed(self.attribute, **kwargs)), + len(self.parent_config_entity._computed(self.attribute, **kwargs)) - reduced_difference) + # Try adding a set to the parent and make sure the children respond + self.confirm_set_added_to_parent(enhanced_difference, reduced_difference, **kwargs) + + # Save a list of all items, including those adopted from the parent. These are either the through instances or normal instances + saved_items = list(self.enhanced_config_entity._computed(self.attribute, **kwargs)) + if self.is_through: + # Remove the pk to make these through items seem new, since we'll clear the originals below + for item in saved_items: + item.pk=None + + # Clear the enhance_config_entity's items + self.enhanced_config_entity._clear(self.attribute) + self.enhanced_config_entity.save() + # We expect pk equality with parent's sets + self.test.assertEqual(map(lambda set: set.pk, self.enhanced_config_entity._computed(self.attribute, **kwargs)), + map(lambda set: set.pk, self.parent_config_entity._computed(self.attribute, **kwargs))) + # Restore the self.enhanced_config_entity items by adding the old computed items + # Even though donor items are part of the saved_items, they should be ignored by the add process, since they are already present + self.enhanced_config_entity._add(self.attribute, *saved_items) + # Ensure that the original number now exist even though the save_sets contained some of the parent's + self.test.assertEqual( + len(self.enhanced_config_entity._computed(self.attribute, **kwargs)), + len(self.parent_config_entity._computed(self.attribute, **kwargs)) + enhanced_difference) + + def confirm_set_added_to_parent(self, enhanced_difference=1, reduced_difference=1, **kwargs): + # Add a new item to parent + generated_item = self.item_generator(self.parent_config_entity) + # We expect the enhanced_region to continue to have 'difference' more items + self.test.assertEqual( + len(self.enhanced_config_entity._computed(self.attribute, **kwargs)), + len(self.parent_config_entity._computed(self.attribute, **kwargs)) + enhanced_difference, + "Expected a difference of {0} between {1} and {2}".format( + enhanced_difference, + self.enhanced_config_entity._computed(self.attribute, **kwargs), + self.parent_config_entity._computed(self.attribute, **kwargs))) + # We expect the reduced_region to continue to have 'difference' fewer items + if self.reduced_config_entity: + self.test.assertEqual( + len(self.reduced_config_entity._computed(self.attribute, **kwargs)), + len(self.parent_config_entity._computed(self.attribute, **kwargs)) - reduced_difference, + "Expected a difference of {0} between {1} and {2}".format( + reduced_difference, + self.reduced_config_entity._computed(self.attribute, **kwargs), + self.parent_config_entity._computed(self.attribute, **kwargs))) + # Remove that last item from the parent + self.parent_config_entity._remove(self.attribute, generated_item) + # Again we expect the config_entities to have updated according to the parent + self.test.assertEqual( + len(self.enhanced_config_entity._computed(self.attribute, **kwargs)), + len(self.parent_config_entity._computed(self.attribute, **kwargs)) + enhanced_difference, + "Expected a difference of {0} between {1} and {2}".format( + enhanced_difference, + self.enhanced_config_entity._computed(self.attribute, **kwargs), + self.parent_config_entity._computed(self.attribute, **kwargs))) + if self.reduced_config_entity: + self.test.assertEqual( + len(self.reduced_config_entity._computed(self.attribute, **kwargs)), + len(self.parent_config_entity._computed(self.attribute, **kwargs)) - reduced_difference, + "Expected a difference of {0} between {1} and {2}".format( + reduced_difference, + self.reduced_config_entity._computed(self.attribute, **kwargs), + self.parent_config_entity._computed(self.attribute, **kwargs))) + diff --git a/footprint/main/tests/test_models/test_feature/__init__.py b/footprint/main/tests/test_models/test_feature/__init__.py new file mode 100644 index 000000000..318f150c8 --- /dev/null +++ b/footprint/main/tests/test_models/test_feature/__init__.py @@ -0,0 +1,35 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from nose import with_setup + +__author__ = 'calthorpe_associates' + +class TestFeature(unittest.TestCase): + + def setup(self): + pass + + def teardown(self): + pass + + @with_setup(setup, teardown) + def test_application_initialization(self): + initialize_table_definitions() + global_config = initialize_global_config() + # Assert that the schema is created + assert PGNamespace.objects.schema_exists(global_config.schema()), "The global schema %s does not exist" % global_config.schema() diff --git a/footprint/main/tests/test_models/test_models.py b/footprint/main/tests/test_models/test_models.py new file mode 100644 index 000000000..4d1f6a83f --- /dev/null +++ b/footprint/main/tests/test_models/test_models.py @@ -0,0 +1,39 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import unittest +from nose import with_setup +from footprint.main.models.database.information_schema import PGNamespace +from footprint.main.models.application_initialization import initialize_table_definitions, initialize_global_config + +__author__ = 'calthorpe_associates' + +class TestModels(unittest.TestCase): + + def setup(self): + pass + + def teardown(self): + pass + + @with_setup(setup, teardown) + def test_application_initialization(self): + initialize_table_definitions() + global_config = initialize_global_config() + # Assert that the schema is created + assert PGNamespace.objects.schema_exists(global_config.schema()), "The global schema %s does not exist" % global_config.schema() + diff --git a/footprint/main/tests/test_models/test_presentation/__init__.py b/footprint/main/tests/test_models/test_presentation/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_models/test_presentation/test_layer_selection.py b/footprint/main/tests/test_models/test_presentation/test_layer_selection.py new file mode 100644 index 000000000..19d9263ae --- /dev/null +++ b/footprint/main/tests/test_models/test_presentation/test_layer_selection.py @@ -0,0 +1,61 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.utils import unittest +from django.contrib.gis.geos import MultiPolygon +from nose import with_setup +from footprint.main.initialization.data_provider import DataProvider +from footprint.main.models import LayerLibrary +from footprint.main.models.application_initialization import minimum_initialization +from footprint.main.models.config.global_config import global_config_singleton +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.layer_selection import get_or_create_dynamic_layer_selection_class_and_table +from footprint import settings + +__author__ = 'calthorpe_associates' + +class TestLayerSelection(unittest.TestCase): + + def setUp(self): + pass + + def teardown(self): + pass + + @with_setup(setUp, teardown) + def test_selection(self): + # For some reason this module with signal receivers doesn't get imported in the test environment. It does normally + import footprint.main.publishing + + # Create a test Scenario + minimum_initialization() + scenario = DataProvider().scenarios()[0] + # Fetch a Default Layer Library instance + layer_library = LayerLibrary.objects.get(key=Keys.LAYER_LIBRARY_DEFAULT, config_entity=scenario) + # Fetch the layer that displays FutureScenarioFeature DbEntity + layer = layer_library.presentationmedium_set.filter(db_entity_key=Keys.DB_ABSTRACT_FUTURE_SCENARIO_FEATURE).all().select_subclasses()[0] + # Create the LayerSelection dynamic subclass + layer_selection_class = get_or_create_dynamic_layer_selection_class_and_table(layer) + + # Get the instance for the user + user = DataProvider().user()['user'] + user_layer_selection = layer_selection_class.objects.update_or_create(user=user, layer=layer)[0] + # Set the geography to the whole world. This should cause all features to be added to the features attribute + user_layer_selection.bounds = global_config_singleton().bounds + feature_class = user_layer_selection.feature_class() + # Assert that the features are all selected + #assert(len(user_layer_selection.features) == len(feature_class.objects.all())) + assert(len(user_layer_selection.selected_features) == len(feature_class.objects.all())) + # Clear the SelectionLayer instance + user_layer_selection.bounds = MultiPolygon([], srid=settings.DEFAULT_SRID) + # Assert that no features are selected + assert(len(user_layer_selection.selected_features) == 0) diff --git a/footprint/main/tests/test_models/test_presentation/test_library.py b/footprint/main/tests/test_models/test_presentation/test_library.py new file mode 100644 index 000000000..8b1039b80 --- /dev/null +++ b/footprint/main/tests/test_models/test_presentation/test_library.py @@ -0,0 +1,49 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.utils import unittest +from footprint.main.tests.data_provider import DataProvider + +__author__ = 'calthorpe_associates' + +class TestLibrary(unittest.TestCase): + """ + Test the ability to add, remove and sort items in a Library instance + """ + + def setup(self): + pass + + def teardown(self): + pass + + def test_full_library(self): + """ + Create a library whose config_entity combines sibling Scenarios and all their ancestral media + :return: + """ + library = DataProvider().joint_library() + for attribute, queryset in { + 'presentationmedium_set':library.presentationmedium_set.all(), + 'media':library.media.all(), + 'db_entities':library.db_entities()}.items(): + assert( + map(lambda instance: instance.pk, library.sorted(queryset, attribute)), + map(lambda instance: instance.pk, queryset.all()) + ) + diff --git a/footprint/main/tests/test_models/test_presentation/test_map.py b/footprint/main/tests/test_models/test_presentation/test_map.py new file mode 100644 index 000000000..2a92902b9 --- /dev/null +++ b/footprint/main/tests/test_models/test_presentation/test_map.py @@ -0,0 +1,36 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.utils import unittest +from nose import with_setup +from footprint.main.tests.data_provider import DataProvider + +__author__ = 'calthorpe_associates' + +class TestMap(unittest.TestCase): + + def setup(self): + pass + + def teardown(self): + pass + + @with_setup(setup, teardown) + def test_map_creation(self): + pass + diff --git a/footprint/main/tests/test_models/test_presentation/test_presentation.py b/footprint/main/tests/test_models/test_presentation/test_presentation.py new file mode 100644 index 000000000..3bc50eec2 --- /dev/null +++ b/footprint/main/tests/test_models/test_presentation/test_presentation.py @@ -0,0 +1,50 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.utils import unittest +from nose import with_setup +from footprint.main.initialization.data_provider import DataProvider +from footprint.main.models import Presentation +from footprint.main.models.application_initialization import minimum_initialization +from footprint.main.models.keys.keys import Keys + +__author__ = 'calthorpe_associates' + +class TestPresentation(unittest.TestCase): + + def setup(self): + pass + + def teardown(self): + pass + + @with_setup(setup, teardown) + def test_default_map_creation(self): + scenarios = DataProvider().scenarios() + smart_scenario = scenarios[0] + # Assert that the Scenario has its default map presentation and not that of its parent + assert len(smart_scenario.presentation_set.filter(key=Keys.LAYER_LIBRARY_DEFAULT)) == 1 + assert smart_scenario.presentation_set.get(key=Keys.LAYER_LIBRARY_DEFAULT).pk != smart_scenario.parent_config_entity.presentation_set.get(key=Keys.LAYER_LIBRARY_DEFAULT).pk + + @with_setup(setup, teardown) + def test_presentation_config_entities(self): + minimum_initialization() + DataProvider().scenarios() + presentations = Presentation.objects.all() + for presentation in presentations: + assert presentation.subclassed_config_entity, "Presentation {0} has no config_entity".format(presentation) diff --git a/footprint/main/tests/test_publishing/__init__.py b/footprint/main/tests/test_publishing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_publishing/test_geoserver.py b/footprint/main/tests/test_publishing/test_geoserver.py new file mode 100644 index 000000000..9820c0ef1 --- /dev/null +++ b/footprint/main/tests/test_publishing/test_geoserver.py @@ -0,0 +1,46 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.utils import unittest +from nose import with_setup +from footprint.main.publishing.geoserver import validate_geoserver_configuration +from footprint.main.tests.data_provider import DataProvider + +__author__ = 'calthorpe_associates' + +class TestGeoserver(unittest.TestCase): + + def setup(self): + pass + + def teardown(self): + pass + + @with_setup(setup, teardown) + def test_geoserver_presentation(self): + """ + The Geoserver observer classes will create a Presentation and publish DbEntity tables and Media styles for + each scenarios and projects. Test here to ensure that they are created as expected + :return: + """ + scenarios = DataProvider().scenarios() + smart_scenario = scenarios[0] + # Assert that the project assets exist + validate_geoserver_configuration(smart_scenario.project) + # Assert that a scenario's assets exist + validate_geoserver_configuration(smart_scenario) diff --git a/footprint/main/tests/test_publishing/test_layer_import.py b/footprint/main/tests/test_publishing/test_layer_import.py new file mode 100644 index 000000000..b4bf4ee8f --- /dev/null +++ b/footprint/main/tests/test_publishing/test_layer_import.py @@ -0,0 +1,41 @@ +import os +import shutil +from django.contrib.auth.models import User +from footprint.main.models.database.information_schema import verify_srid +from django.conf import settings +from footprint.main.publishing.shapefile_processor import unpack_shapefile, import_shapefile_to_db + +__author__ = 'calthorpe_associates' + +sample_data_path = os.path.join('srv', 'calthorpe', 'urbanfootprint', 'footprint', 'main', 'samples') + +test_shps = [ + dict( + zip_path=os.path.join(sample_data_path, 'ZipCodes2009.zip'), + srid=102642, + new_name='test_zipcodes', + user=User.objects.get(username='test') + ), + dict( + zip_path=os.path.join(sample_data_path, 'TAZ07_w_tahoe.zip'), + srid=102642, + new_name='test_taz', + user=User.objects.get(username='test') + ) +] + +def test_shp_uploads(): + for shp in test_shps: + load_test_shp(**shp) + +def load_test_shp(zip_path="/srv/calthorpe/urbanfootprint/footprint/main/samples/ZipCodes2009.zip", + srid=102642, + new_name='my_zipcodes', + user=User.objects.get(username='test')): + spatial_ref = verify_srid(srid) + shutil.copy(zip_path, settings.MEDIA_ROOT) + shapefile_zip = os.path.join(settings.MEDIA_ROOT, os.path.basename(zip_path)) + + shp, files_to_import = unpack_shapefile(shapefile_zip, 'zipcodes', user) + shp = os.path.join(settings.MEDIA_ROOT, shp) + import_shapefile_to_db(shp, new_name, spatial_ref) \ No newline at end of file diff --git a/footprint/main/tests/test_publishing/test_layer_publishing.py b/footprint/main/tests/test_publishing/test_layer_publishing.py new file mode 100644 index 000000000..07d73c279 --- /dev/null +++ b/footprint/main/tests/test_publishing/test_layer_publishing.py @@ -0,0 +1,50 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.core.management import call_command +from django.utils import unittest +from nose.tools import assert_equal + +from footprint.main.initialization.fixture import LayerConfigurationFixture +from footprint.main.publishing.layer_initialization import LayerLibraryKey, LayerTag +from footprint.client.configuration.utils import resolve_fixture +from footprint.main.models import Region +from footprint.main.models.application_initialization import minimum_initialization +from footprint.main.models.config.scenario import FutureScenario + + +class TestLayerPublishing(unittest.TestCase): + + def test_layer_setup(self): + minimum_initialization() + region = Region.objects.get() + # Call footprint_init to update the layer publishing in case something changed since the last test run + # and we didn't clear the database + call_command('footprint_init', skip=True, layer=True) + client_layer_configuration = resolve_fixture( + "publishing", + "layer", + LayerConfigurationFixture, + region.schema()) + future_scenario = FutureScenario.objects.all()[0] + layer_library = future_scenario.presentation_set.filter(key=LayerLibraryKey.DEFAULT)[0] + # Make sure that all client layers exist + assert_equal( + len(layer_library.presentationmedium_set.all()), + len(client_layer_configuration.layers())) + # Make sure specifically background layers exist + assert_equal( + len(layer_library.presentation_media.filter(tag__contains=[LayerTag.BACKGROUND_IMAGERY])), + len(client_layer_configuration.background_layers())) + # Make sure specifically main layers exist + assert_equal( + len(layer_library.presentation_media.filter(tag__contains=[LayerTag.MAIN])), + len(client_layer_configuration.background_layers())) diff --git a/footprint/main/tests/test_publishing/test_results.py b/footprint/main/tests/test_publishing/test_results.py new file mode 100644 index 000000000..55613ff06 --- /dev/null +++ b/footprint/main/tests/test_publishing/test_results.py @@ -0,0 +1,29 @@ +from django.utils import unittest +from nose import with_setup +from footprint.main.initialization.data_provider import DataProvider + + +class TestResults(unittest.TestCase): + """ + The TileStache classes will observe the layer library and the standard list of scenarios, and keep a + config string to render all of the possible tables when they are requested. These tests make sure that + system is working properly. + :return: + """ + + def setup(self): + pass + + def teardown(self): + pass + + + @with_setup(setup, teardown) + def test_create_results(self): + """ + :return: + """ + + scenario = DataProvider().scenarios()[0] + scenario.save() + diff --git a/footprint/main/tests/test_publishing/test_tilestache.py b/footprint/main/tests/test_publishing/test_tilestache.py new file mode 100644 index 000000000..13ab52635 --- /dev/null +++ b/footprint/main/tests/test_publishing/test_tilestache.py @@ -0,0 +1,73 @@ +import os +from django.utils import unittest +from nose import with_setup +from footprint.main.initialization.data_provider import DataProvider +from footprint.main.models.presentation.layer import Layer +from footprint.main.models.keys.keys import Keys +from footprint.main.models.presentation.tilestache_config import * +from footprint.main.publishing.tilestache_initialization import update_or_create_built_form_template + + +class TestTileStache(unittest.TestCase): + """ + The TileStache classes will observe the layer library and the standard list of scenarios, and keep a + config string to render all of the possible tables when they are requested. These tests make sure that + system is working properly. + :return: + """ + + def setup(self): + # DataProvider().import_parcels() + update_or_create_built_form_template() + pass + + def teardown(self): + pass + + @with_setup(setup, teardown) + def fake_tilestache_config(self): + """ + using a totally fake style and layer, generates the Mapnik config file for rendering a layer as raster. + test makes sure that CartoCSS is working on its local Node.js + """ + testXML_creation = sampleMapfile() + assert os.path.exists(testXML_creation) + pass + + def create_test_layer(self): + """ + Creates a Layer object to use in tests + :return: The Layer object + """ + + scenario = DataProvider().scenarios()[0] + scenario.save() + layer_db_entity = scenario.selections['db_entities'][Keys.DB_SCENARIO_BUILT_FORM_LAYER] + layer_presentation = scenario.presentation_set(key=Keys.LAYER_LIBRARY_DEFAULT)[0] + layer_presentationmedium = layer_presentation.presentationmedium_set.filter( + db_entity_key=Keys.DB_SCENARIO_BUILT_FORM_LAYER)[0] + + table = '''"{0}"."{1}"'''.format(layer_db_entity.schema, layer_db_entity.table) + layer, created, updated = Layer.objects.update_or_create( + presentation=layer_presentation, + medium=layer_presentationmedium.medium, + db_entity_key=layer_db_entity.key, + name="layer_{0}".format(layer_db_entity.key), + configuration={'table': table, 'style': {}} + ) + assert Layer.objects.all().exists() is True + return layer + + + @with_setup(setup, teardown) + def config_object(self): + scenario = DataProvider().scenarios()[0] + config, created, updated = TileStacheConfig.objects.update_or_create(name='default') + config.modify_config() + x = config + + @with_setup(setup, teardown) + def run_all_tests(self): + self.fake_tilestache_config() + self.config_object() + diff --git a/footprint/main/tests/test_resources/test_config_entities/__init__.py b/footprint/main/tests/test_resources/test_config_entities/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_resources/test_presentations/__init__.py b/footprint/main/tests/test_resources/test_presentations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/tests/test_technology/test_deep_merge.py b/footprint/main/tests/test_technology/test_deep_merge.py new file mode 100644 index 000000000..57a0aa936 --- /dev/null +++ b/footprint/main/tests/test_technology/test_deep_merge.py @@ -0,0 +1,35 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import unittest +from nose import with_setup +from nose.tools import assert_equal +from footprint.main.lib.functions import deep_merge + +__author__ = 'calthorpe_associates' + +class TestFunctions(unittest.TestCase): + def setup(self): + pass + + def teardown(self): + pass + + @with_setup(setup, teardown) + def test(self): + result = deep_merge({1:{1:1}, 'a':{'b':{'b':'b'}}}, {1:{2:2}, 2:2}, {3:3, 'a':{'b':{'c':'c'}}}) + assert_equal(result, {1:{1:1, 2:2}, 2:2, 3:3, 'a':{'b':{'b':'b', 'c':'c'}}}) diff --git a/footprint/main/tilestache_views.py b/footprint/main/tilestache_views.py new file mode 100644 index 000000000..ddc0936cc --- /dev/null +++ b/footprint/main/tilestache_views.py @@ -0,0 +1,30 @@ +from django.http import HttpResponse +from footprint.main.models import TileStacheConfig + +__author__ = 'calthorpe_associates' + +import TileStache + +def tilestache_tiles(request, layer_name, z, x, y, extension): + """ + :param request: + :param layer_name: + :param z: + :param x: + :param y: + :param extension: + :return: + + Proxy to tilestache + {X} - coordinate column. + {Y} - coordinate row. + {B} - bounding box. + {Z} - zoom level. + {S} - host. + """ + + config = TileStacheConfig.objects.filter(name='default')[0].config + path_info = "%s/%s/%s/%s.%s" % (layer_name, z, x, y, extension) + coord, extension = TileStache.splitPathInfo(path_info)[1:] + mimetype, content = TileStache.getTile(config.layers[layer_name], coord, extension) + return HttpResponse(content, mimetype=mimetype) diff --git a/footprint/main/upload_progress_cached_handler.py b/footprint/main/upload_progress_cached_handler.py new file mode 100644 index 000000000..8683df604 --- /dev/null +++ b/footprint/main/upload_progress_cached_handler.py @@ -0,0 +1,63 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.core.files.fileuploadhandler import FileUploadHandler +from django.core.cache import cache + +#http://www.laurentluce.com/posts/upload-to-django-with-progress-bar-using-ajax-and-jquery/ +class UploadProgressCachedHandler(FileUploadHandler): + """ + Tracks progress for file uploads. + The http post request must contain a header or query parameter, 'X-Progress-ID' + which should contain a unique string to identify the upload to be tracked. + """ + + def __init__(self, request=None): + super(UploadProgressCachedHandler, self).__init__(request) + self.progress_id = None + self.cache_key = None + + def handle_raw_input(self, input_data, META, content_length, boundary, encoding=None): + self.content_length = content_length + if 'X-Progress-ID' in self.request.GET : + self.progress_id = self.request.GET['X-Progress-ID'] + elif 'X-Progress-ID' in self.request.META: + self.progress_id = self.request.META['X-Progress-ID'] + if self.progress_id: + self.cache_key = "%s_%s" % (self.request.META['REMOTE_ADDR'], self.progress_id ) + cache.set(self.cache_key, { + 'length': self.content_length, + 'uploaded' : 0 + }) + + def new_file(self, field_name, file_name, content_type, content_length, charset=None): + pass + + def receive_data_chunk(self, raw_data, start): + if self.cache_key: + data = cache.get(self.cache_key) + data['uploaded'] += self.chunk_size + cache.set(self.cache_key, data) + return raw_data + + def file_complete(self, file_size): + pass + + def upload_complete(self): + if self.cache_key: + cache.delete(self.cache_key) diff --git a/footprint/main/urls.py b/footprint/main/urls.py new file mode 100644 index 000000000..c2636b1fc --- /dev/null +++ b/footprint/main/urls.py @@ -0,0 +1,140 @@ +# urbanfootprint-california (v1.0), land use scenario development and modeling system. +# +# Copyright +# (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.conf.urls import include, url, patterns +from django.views.generic.simple import direct_to_template + +from tastypie.api import Api +from footprint.main.publishing.data_export_publishing import export_layer, get_export_result +from footprint.main.resources.analytic_resources import CoreResource #, FiscalResource +from footprint.main.resources.built_form_resources import BuiltFormResource, BuiltFormSetResource, PlacetypeResource, \ + PlacetypeComponentResource, BuildingUseDefinitionResource, \ + BuildingUsePercentResource, BuildingAttributeSetResource, BuiltFormExampleResource, PlacetypeComponentPercentResource, PrimaryComponentResource, PrimaryComponentPercentResource +from footprint.main.resources.category_resource import CategoryResource +from footprint.main.resources.client.client_land_use_definition_resource import ClientLandUseDefinitionResource + + +from footprint.main.resources.config_entity_resources import GlobalConfigResource, RegionResource, ProjectResource, ScenarioResource, ConfigEntityResource, FutureScenarioResource, BaseScenarioResource +from footprint.main.resources.db_entity_resources import DbEntityResource, DbEntityInterestResource, InterestResource +from footprint.main.resources.feature_resources import FeatureResource +from footprint.main.resources.flat_built_form_resource import FlatBuiltFormResource +from footprint.main.resources.layer_resources import LayerResource, LayerLibraryResource +from footprint.main.resources.layer_selection_resource import LayerSelectionResource +from footprint.main.resources.medium_resources import MediumResource +from footprint.main.resources.policy_resources import PolicyResource, PolicySetResource +from footprint.main.resources.presentation_medium_resource import PresentationMediumResource +from footprint.main.resources.presentation_resources import PresentationResource +from footprint.main.resources.result_resources import ResultLibraryResource, ResultResource +from footprint.main.resources.tag_resource import TagResource +from footprint.main.resources.user_resource import UserResource +from footprint.main.tilestache_views import tilestache_tiles +from footprint.main.views import upload + +from django.conf import settings + +urlpatterns = patterns( + '', + url(r'^$', direct_to_template, {'template': 'footprint/index.html'}), + url(r'^(?P[^/]+)/export_layer/(?P[^/]+)', export_layer), + url(r'^(?P[^/]+)/get_export_result/(?P[^/]+)', get_export_result), + + url(r'^tiles/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)/(?P[^/]+)\.(?P.+)$', tilestache_tiles, + name='tiles_url'), + url(r'^upload/.*', upload), +) + + +# All tastypie resources need to be listed here +v1_api = Api(api_name="v{0}".format(settings.API_VERSION)) +v1_api.register(UserResource()) +v1_api.register(ConfigEntityResource()) +v1_api.register(GlobalConfigResource()) +v1_api.register(RegionResource()) +v1_api.register(ProjectResource()) +v1_api.register(ScenarioResource()) +v1_api.register(FutureScenarioResource()) +v1_api.register(BaseScenarioResource()) +v1_api.register(DbEntityResource()) +v1_api.register(DbEntityInterestResource()) +v1_api.register(InterestResource()) + + +v1_api.register(PlacetypeResource()) +v1_api.register(PlacetypeComponentResource()) +v1_api.register(PlacetypeComponentPercentResource()) +v1_api.register(PrimaryComponentResource()) +v1_api.register(PrimaryComponentPercentResource()) + +v1_api.register(BuildingUseDefinitionResource()) +v1_api.register(BuildingUsePercentResource()) +v1_api.register(BuildingAttributeSetResource()) +v1_api.register(BuiltFormSetResource()) +v1_api.register(BuiltFormExampleResource()) +v1_api.register(BuiltFormResource()) + +v1_api.register(PolicyResource()) +v1_api.register(PolicySetResource()) + +v1_api.register(PresentationResource()) +v1_api.register(LayerLibraryResource()) +v1_api.register(ResultLibraryResource()) + +v1_api.register(MediumResource()) + +v1_api.register(PresentationMediumResource()) +v1_api.register(LayerResource()) +v1_api.register(LayerSelectionResource()) + +v1_api.register(ResultResource()) + +v1_api.register(CategoryResource()) +v1_api.register(TagResource()) +v1_api.register(CoreResource()) +#v1_api.register(FiscalResource()) + +#Built Form Resources +v1_api.register(ClientLandUseDefinitionResource()) +v1_api.register(FlatBuiltFormResource()) + +v1_api.register(FeatureResource()) + +# Django Rest API +urlpatterns += patterns( + '', + (r'^api/', include(v1_api.urls)), +) + +# Cross-domain proxying if we need it +#urlpatterns += patterns('', +# (r'^(?P.*)$', 'httpproxy.views.proxy'), +#) +#urlpatterns += staticfiles_urlpatterns() #this is meant for debug only + +#from celery.task import PingTask +#from djcelery import views as celery_views + +#celery webhook +#urlpatterns += patterns("", +# url(r'^apply/(?P.+?)/', celery_views.apply), +# url(r'^ping/', celery_views.task_view(PingTask)), +# url(r'^(?P[\w\d\-]+)/done/?$', celery_views.is_task_successful, +# name="celery-is_task_successful"), +# url(r'^(?P[\w\d\-]+)/status/?$', celery_views.task_status, +# name="celery-task_status"), +#) diff --git a/footprint/main/utils/__init__.py b/footprint/main/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/footprint/main/utils/discard_geos_exceptions.py b/footprint/main/utils/discard_geos_exceptions.py new file mode 100644 index 000000000..f99b68601 --- /dev/null +++ b/footprint/main/utils/discard_geos_exceptions.py @@ -0,0 +1,21 @@ +__author__ = 'calthorpe' + +# https://gist.github.com/tobych/6372218 +# Monkey patch to work around https://code.djangoproject.com/ticket/13843 + +import django +from functools import wraps + +def discard_exceptions(f): + @wraps(f) + def wrapper(*args, **kwds): + try: + f(*args, **kwds) + except (AttributeError, TypeError): + pass + return wrapper + +django.contrib.gis.geos.prototypes.threadsafe.GEOSContextHandle.__del__ = \ + discard_exceptions(django.contrib.gis.geos.prototypes.threadsafe.GEOSContextHandle.__del__) +django.contrib.gis.geos.prototypes.io.IOBase.__del__ = \ + discard_exceptions(django.contrib.gis.geos.prototypes.io.IOBase.__del__) \ No newline at end of file diff --git a/footprint/main/utils/dynamic_subclassing.py b/footprint/main/utils/dynamic_subclassing.py new file mode 100644 index 000000000..7a741bac9 --- /dev/null +++ b/footprint/main/utils/dynamic_subclassing.py @@ -0,0 +1,362 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +import logging +import re +import string +from django.db import transaction +from south.db import db +from django.db import models, DatabaseError +from django.db.models.signals import class_prepared +from tastypie.resources import ModelDeclarativeMetaclass +from footprint.main.lib.functions import merge, map_to_dict +from footprint.main.managers.geo_inheritance_manager import GeoInheritanceManager +# Creates a model based on the given abstractClass and customTable name +# This allows us to map dynamically created tables such as those in the inputs_outputs_* schemas +from footprint.main.models.database.information_schema import InformationSchema +from footprint.main.utils.utils import parse_schema_and_table, resolve_model, resolve_module_attr, resolvable_module_attr_path + +logger = logging.getLogger(__name__) + +def dynamic_model_class(abstract_class, schema, table, class_name=None, class_attrs={}, fields={}, scope=None, related_class_lookup={}): + """ + :param abstract_class: The abstract class to subclass. This doesn't actually have to be abstract and could simply be object + :param schema: The schema of the table. + :param table: The table name. Used for Meta.db_table + :param class_name: The name to use for the class. If not specified the name will be formed from the table_name + :param class_attrs: Nonmodel attributes to add to the subclass + :param fields: Model fields to add to the subclass, such as ForeignKey, ManyToMany, etc + :param scope: Optional. A model instance which represents the scope of this subclass. The id is used to form the class name + :param related_class_lookup: used in conjunction with class_attrs with the form attr__id. For instance, if a class_attr config_entity__id + with value 56 is specified, related_class_lookup will be dict(config_entity='footprint.main.models.config.ConfigEntity') to + specify what model class resolved the related id. The class then gets a getter property that returns the resolved version of the id. + This is all to prevent storing the instance as a class attr, which is expensive to pickle elsewhere in the system. + class called config_entity which uses the id stored in + :return: + """ + + computed_name = class_name or get_dynamic_model_class_name(abstract_class, scope.id if isinstance(scope, models.Model) else scope or '') + # Return the model class if already created + resolved_model = resolve_model('main.%s' % computed_name) + if resolved_model: + #logger.debug('Found model class {0}'.format(computed_name) + return resolved_model + else: + logger.debug('Creating model class {0}'.format(computed_name)) + + # Source: http://stackoverflow.com/questions/1735434/class-level-read-only-properties-in-python + # We want do create properties scoped to the class below, but classes by default just return the Property object + # itself. This chnages that behavior + class ClassProperty(property): + def __get__(self, cls, owner): + return self.fget.__get__(None, owner)() + + class FootprintMetaclass(abstract_class.__metaclass__): + """Create a metaclass that overrides the name of the class it creates based on the given table name""" + def __new__(cls, name, bases, attrs): + + # Register any additional model fields specified in fields + def add_field(sender, **kwargs): + if sender.__name__ == computed_name: + for field_name, field in fields.items(): + field.contribute_to_class(sender, field_name) + class_prepared.connect(add_field) + + def create_class_property(class_attr): + related_attr = class_attr.split('__')[0] + related_class_name = related_class_lookup.get(related_attr, None) + if not related_class_name: + raise Exception("Expected related_class_lookup to contain %s, since class_attrs contain %s" % (related_attr, class_attr) ) + related_class = resolve_module_attr(related_class_name) + # Create the getter property that uses the class manager to lookup up the related model by id + def getter(cls): + return related_class.objects.get(id=getattr(cls, class_attr)) + return ClassProperty(classmethod(getter)) + + # Create class-level getter properties to resolve things like the config_enity since we only store the id + class_properties = map_to_dict( + lambda class_attr: [class_attr.split('__')[0], create_class_property(class_attr)], + filter(lambda class_attr: class_attr.endswith('__id'), class_attrs)) + + return models.base.ModelBase.__new__( + cls, + computed_name, + (abstract_class,), + # Merge the manager objects (if the abstract_class has one) with attrs and class_attrs + merge(dict(objects=abstract_class.objects.__class__()) if hasattr(abstract_class, 'objects') else {}, + attrs, + class_attrs, + class_properties)) + # Create the custom class using the configured meta class + class GenericModelClass(object): + __metaclass__ = FootprintMetaclass + + objects = GeoInheritanceManager() + + class Meta: + # Set the table name + db_table = '"{0}"."{1}"'.format(schema, table) + app_label = 'main' + + return GenericModelClass + +def get_dynamic_resource_class(super_class, model_class, fields={}, meta_fields={}): + """ + Subclass the super_class and change the queryset to the model_class's and abstract to False + :param super_class: The Resource class to subclass + :param model_class: The concrete subclassed model class which we want the subclassed Resource class to query + :param fields: Additional fields to give the class. + :param fields: Additional meta fields, such as fields or excludes + :return: + """ + class_name = get_dynamic_resource_class_name(super_class, model_class) + try: + # Return the class if it was already created + modname = globals()['__name__'] + existing_class = resolve_module_attr('%s.%s' % (modname, class_name)) + if existing_class: + return existing_class + except: + pass + + return ModelDeclarativeMetaclass( + class_name, + (super_class,), + merge( + fields, + dict( + Meta=type( + 'Meta', + (super_class.Meta,), + merge( + dict( + queryset=model_class.objects.all(), + abstract=False), + meta_fields) + ) + ) + ) + ) + +# From http://dynamic-models.readthedocs.org/en/latest/topics/database-migration.html#topics-database-migration +def create_tables_for_dynamic_classes(*model_classes): + """ + Creates the table for the dynamic model class if needed + :param model_classes: 0 or more model classes for which to create a table + :return: + """ + + for model_class in model_classes: + if InformationSchema.objects.table_exists(*parse_schema_and_table(model_class._meta.db_table)): + continue + + info = "Model class table {model_class} doesn't exist -- creating it \n" + logger.info(info.format(model_class=model_class._meta.db_table)) + + fields = [(f.name, f) for f in model_class._meta.local_fields] + table_name = model_class._meta.db_table + db.create_table(table_name, fields) + + # some fields (eg GeoDjango) require additional SQL to be executed + # Because of the poor Django/GeoDjango support for schemas, we have to manipulate the GeoDjango sql here so that the table is resolved to the correct schema, sigh + if len(table_name.split('.'))==2: + schema, table = parse_schema_and_table(table_name) + for i, sql in enumerate(db.deferred_sql): + # Replace the POSTGIS single argument with two arguments + # TODO this stupidly assumes that all deferred sql is POSTGIS + # Substitution for '"schema"."table"' to 'schema','table'. This is for AddGeometryColumn + db.deferred_sql[i] = re.sub("'{0}'".format(table_name), "'{0}','{1}'".format(schema, table), sql) + # Substitution for "schema"."table" to schema.table. This is for CREATE INDEX + db.deferred_sql[i] = re.sub("{0}".format(table_name), "{0}.{1}".format(schema, table), db.deferred_sql[i]) + # Substitution for "schema"."tableHEX". Some indexes add random hex to the table name inside the double quotes. They may also truncate the table name, so just capture everything between "s + # Also truncate to 64 characters the schema name minus the length of the table name, favoring the end of the schema which is most unique + db.deferred_sql[i] = re.sub(r'"(".*)"\."(.*") ON', r'\1.\2 ON'.format(schema, table), db.deferred_sql[i]) + # Last ditch effort to remove extra " when we can't match generated index + db.deferred_sql[i] = re.sub(r'""', r'"', db.deferred_sql[i]) + if string.find(db.deferred_sql[i], 'CREATE INDEX') == 0: + subs = db.deferred_sql[i] + # Truncate the index name. This could be done more elegantly + db.deferred_sql[i] = subs[0:14] + subs[14:string.index(subs, '" ON')][-63:] + subs[string.index(subs, '" ON'):] + + try: + db.execute_deferred_sql() + except Exception, e: + raise Exception("The table {table_name} was not created. Original exception: {message}. Deferred sql calls: {sql}".format(table_name=model_class._meta.db_table, message=e.message, sql='\n'.join(db.deferred_sql))) + # TODO I don't know if or when this is needed. + if transaction.is_managed(): + transaction.commit() + if not InformationSchema.objects.table_exists(*parse_schema_and_table(model_class._meta.db_table)): + raise Exception("The table {table_name} was not created".format(table_name=model_class._meta.db_table)) + info = "Model class table {model_class} created\n" + logger.info(info.format(model_class=model_class._meta.db_table)) + +def drop_tables_for_dynamic_classes(*model_classes): + for model_class in model_classes: + full_table_name = model_class._meta.db_table + schema, table = parse_schema_and_table(full_table_name) + if InformationSchema.objects.table_exists(schema, table): + try: + db.delete_table(full_table_name) + except DatabaseError, e: + raise Exception('full_table_name: {full_table_name}. Original exception: {e}'.format(full_table_name=full_table_name, e=e)) + +def get_dynamic_resource_class_name(super_class, model_class): + # Form the name from the model class's name schema portion + return "{0}{1}".format(model_class.__name__, super_class.__name__) + +def get_dynamic_model_class_name(abstract_class, scope_id): + """ + Returns the naming used for a dynamic model class, which is always based on the table name + :param abstract_class: The table name in the form "'schema'.'table'" + :param scope_id: The id of the scope of the dynamic subclass, such as a ConfigEntity id + :return: + """ + return "{0}{1}".format(abstract_class.__name__, scope_id) + +def resolve_field(model_class, field_name): + try: + return filter(lambda field: field.name == field_name, model_class._meta.fields)[0] + except Exception, e: + raise Exception("Model class {model_class} has no field {field_name}".format(model_class=model_class, field_name=field_name)) + +def resolve_field_of_type(model, target_class): + return filter(lambda field: field.rel and field.rel.to and field.rel.to == target_class, + model._meta.fields)[0] + +def resolve_queryable_name_of_type(model, target_class): + results = filter(lambda field: field.rel and field.rel.to and field.rel.to == target_class, + model._meta.fields) + if len(results) == 1: + return results[0].name + # Search RelatedObject for a matching model. These are tuples, hence the [0]s + m2m_results = filter(lambda related_object: related_object[0].model == target_class, + model._meta.get_all_related_m2m_objects_with_model()) + if len(m2m_results) == 1: + return m2m_results[0][0].var_name + raise Exception("No queryable name matching target_class %s for model %s" % (target_class, model)) + +def create_join(source_class, source_field, related_class, related_field, **kwargs): + """ + Creates a join between two classes without using a modeled Django association. We do this to in order to populate a Through table manually during import, or two simply create + a join query that Django does not support through its related field mechanisms. + Always try to use Django's implicit joining with filter() + extra(select=...) to create a join before resorting to this. Also try filter() with F() functions (see Django docs) + :param source_class: + :param source_field: + :param related_class: + :param related_field: + :param **kwargs: Optional arguments for join + 'extra' dict of extras to select + 'join_on_base' if True use the base class for the join + 'join_related_on_base' if True use the base class of the join class for the join + :return: + """ + + # We either want to join to the related_class or its base, the latter case being when the related_class + # inherits the field that we want to join + resolved_related_class = related_class.__base__ if kwargs.get('join_related_on_base', False) else related_class + + selections = source_class.objects.extra(**merge(dict( + select={'related_pk': '{join_class_table}.{join_class_pk}'.format( + join_class_table=resolved_related_class._meta.db_table, join_class_pk=resolved_related_class._meta.pk.column) + }), + kwargs.get('extra', {}) or {}) + ) + # setup initial FROM clause + selections.query.join((None, source_class._meta.db_table, None, None)) + + if kwargs.get('join_on_base', False): + # Manually join in the base model so that is joins before the join below + parent_field = source_class._meta.parents.values()[0] + connection = ( + source_class._meta.db_table, + source_class.__base__._meta.db_table, + parent_field.column, + 'id' + ) + selections.query.join(connection) + + # join to join class + connection = ( + (source_class.__base__ if kwargs.get('join_on_base', False) else source_class)._meta.db_table, + resolved_related_class._meta.db_table, + source_field.column, + related_field.column, + ) + selections.query.join(connection) + + return selections + + +def create_join_across_association(feature_class, association_field_name): + + association_field = getattr(feature_class._meta, association_field_name) + association_class = association_field.through.blah + + features = feature_class.objects.extra( + select={'pk': '{association_class_table}.{association_class_pk}'.format( + association_class_table=association_class._meta.db_table, association_class_pk=association_class.pk.column) + } + ) + # setup intial FROM clause + feature_class.query.join((None, feature_class._meta.db_table, None, None)) + + # join to through class + primary_connection = ( + feature_class._meta.db_table, + association_field.m2m_db_table(), + feature_class._meta.pk.column, + association_field.m2m_column_name(), + ) + feature_class.query.join(primary_connection, promote=True) + + # join to association class + reverse_connection = ( + association_field.m2m_db_table(), + association_class._meta.db_table, + association_field.m2m_reverse_name(), + association_class._meta.pk.column, + ) + feature_class.query.join(reverse_connection, promote=True) + +class JoinRelationship(object): + def __init__(self, source_class, related_field_name, related_field_configuration, + query=None, filter_dict=None, extra=None, custom_join=None, join_on_base=False, join_related_on_base=False): + self.source_class = source_class + self.related_field = getattr(source_class, related_field_name).field + self.related_class = self.related_field.rel.to + self.query = query + self.filter_dict = filter_dict + self.extra = extra + self.custom_join = custom_join + self.join_on_base = join_on_base + self.join_related_on_base = join_related_on_base + + self.source_class_join_field_name = related_field_configuration.get('source_class_join_field_name', None) + self.related_class_join_field_name = related_field_configuration.get('related_class_join_field_name', None) + +class SingleJoinRelationship(JoinRelationship): + def __init__(self, source_class, related_field_name, related_field_configuration, query=None, filter_dict=None, extra=None, custom_join=None, join_on_base=None, join_related_on_base=None): + super(SingleJoinRelationship, self).__init__(source_class, related_field_name, related_field_configuration, + query=query, filter_dict=filter_dict, extra=extra, custom_join=custom_join, join_on_base=join_on_base, join_related_on_base=join_related_on_base) + +class ManyJoinRelationship(JoinRelationship): + def __init__(self, source_class, related_field_name, related_field_configuration, query=None, filter_dict=None, extra=None, custom_join=None, join_on_base=None, join_related_on_base=None): + super(ManyJoinRelationship, self).__init__(source_class, related_field_name, related_field_configuration, + query=query, filter_dict=filter_dict, extra=extra, custom_join=custom_join, join_on_base=join_on_base, join_related_on_base=join_related_on_base) + + # Extract field and column info about the through class + self.through_class = self.related_field.rel.through + self.through_class_self_field = resolve_field_of_type(self.through_class, source_class) + self.through_class_self_column_name = self.through_class_self_field.column + + self.through_class_related_field = resolve_field_of_type(self.through_class, self.related_class) + self.through_class_related_column_name = self.through_class_related_field.column + diff --git a/footprint/main/utils/function_utils.py b/footprint/main/utils/function_utils.py new file mode 100644 index 000000000..4cc5b61e7 --- /dev/null +++ b/footprint/main/utils/function_utils.py @@ -0,0 +1,51 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +# http://stackoverflow.com/questions/196960/can-you-list-the-keyword-arguments-a-python-function-receives +# >>> import inspect +# >>> def func(a,b,c=42, *args, **kwargs): pass +# >>> inspect.getargspec(func) +# (['a', 'b', 'c'], 'args', 'kwargs', (42,)) +# If you want to know if its callable with a particular set of args, you need the args without a default already specified. These can be got by: +import inspect +from footprint.main.lib.functions import map_to_dict + + +def get_valued_arg_dict(func): + """ + Returns the args with values specified. + :param func: + :return: + """ + args, varargs, varkw, defaults = inspect.getargspec(func) + reversed_defaults = reversed(defaults) + return map_to_dict(lambda default_value: [args.pop(), default_value], reversed_defaults) + +def get_required_args(func): + args, varargs, varkw, defaults = inspect.getargspec(func) + if defaults: + args = args[:-len(defaults)] + return args # *args and **kwargs are not required, so ignore them. + +#Then a function to tell what you are missing from your particular dict is: +def missing_args(func, argdict): + return set(get_required_args(func)).difference(argdict) + +#Similarly, to check for invalid args, use: +def invalid_args(func, argdict): + args, varargs, varkw, defaults = inspect.getargspec(func) + if varkw: return set() # All accepted + return set(argdict) - set(args) + +#And so a full test if it is callable is : +def is_callable_with_args(func, argdict): + return not missing_args(func, argdict) and not invalid_args(func, argdict) diff --git a/footprint/main/utils/inline_inspectdb.py b/footprint/main/utils/inline_inspectdb.py new file mode 100644 index 000000000..959aad83f --- /dev/null +++ b/footprint/main/utils/inline_inspectdb.py @@ -0,0 +1,178 @@ +from django.contrib.gis.db.models import GeometryField +from footprint.main.utils.utils import parse_schema_and_table + +__author__ = 'calthorpe_associates' + +import keyword +from django.db import connections +# KEEP all below for eval +from django.contrib.gis.db import models + +class InlineInspectDb(): + """ + Inline version of Django management tool inspectdb. This is a copy of that logic retrofited + to produce the fields of a class based on introspection of a database table + """ + + requires_model_validation = False + db_module = 'django.db' + + @classmethod + def get_table_description(self, cursor, table, schema, connection): + """ + Returns a description of the table, with the DB-API cursor.description interface." + As cursor.description does not return reliably the nullable property, + we have to query the information_schema (#7783) + """ + cursor.execute(""" + SELECT column_name, is_nullable + FROM information_schema.columns + WHERE table_name = %s AND table_schema = %s""", [table, schema]) + null_map = dict(cursor.fetchall()) + cursor.execute("SELECT * FROM %s.%s LIMIT 1" % + (connection.ops.quote_name(schema), connection.ops.quote_name(table))) + return [tuple([item for item in line[:6]] + [null_map[line[0]]==u'YES']) + for line in cursor.description] + + @classmethod + def get_fields(cls, table_name): + """ + Modifies the original handle_inspection to instead get all the fields of the table described by the db_entity + :param db_entity: + :return: A dict keyed by field named and valued by models.Field or variant instance + """ + + connection = connections['default'] + + table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '') + + cursor = connection.cursor() + + try: + relations = connection.introspection.get_relations(cursor, table_name) + except NotImplementedError: + relations = {} + try: + indexes = connection.introspection.get_indexes(cursor, table_name) + except NotImplementedError: + indexes = {} + + schema, table = parse_schema_and_table(table_name) + # Fill this dict with field definitions + fields = {} + for i, row in enumerate(cls.get_table_description(cursor, table, schema, connection)): + column_name = row[0] + att_name = column_name.lower() + comment_notes = [] # Holds Field notes, to be displayed in a Python comment. + extra_params = {} # Holds Field parameters such as 'db_column'. + + # If the column name can't be used verbatim as a Python + # attribute, set the "db_column" for this Field. + if ' ' in att_name or '-' in att_name or keyword.iskeyword(att_name) or column_name != att_name: + extra_params['db_column'] = column_name + + # Add primary_key and unique, if necessary. + if column_name in indexes: + if indexes[column_name]['primary_key']: + extra_params['primary_key'] = True + elif indexes[column_name]['unique']: + extra_params['unique'] = True + + # Modify the field name to make it Python-compatible. + if ' ' in att_name: + att_name = att_name.replace(' ', '_') + comment_notes.append('Field renamed to remove spaces.') + + if '-' in att_name: + att_name = att_name.replace('-', '_') + comment_notes.append('Field renamed to remove dashes.') + + if column_name != att_name: + comment_notes.append('Field name made lowercase.') + + if i in relations: + rel_to = relations[i][1] == table_name and "'cls'" or table2model(relations[i][1]) + field_type = 'ForeignKey(%s' % rel_to + if att_name.endswith('_id'): + att_name = att_name[:-3] + else: + extra_params['db_column'] = column_name + else: + # Calling `get_field_type` to get the field type string and any + # additional paramters and notes. + field_type, field_params, field_notes = cls.get_field_type(connection, table_name, row) + extra_params.update(field_params) + comment_notes.extend(field_notes) + + field_type += '(' + + if keyword.iskeyword(att_name): + att_name += '_field' + comment_notes.append('Field renamed because it was a Python reserved word.') + + if att_name[0].isdigit(): + att_name = 'number_%s' % att_name + extra_params['db_column'] = unicode(column_name) + comment_notes.append("Field renamed because it wasn't a " + "valid Python identifier.") + + # Don't output 'id = meta.AutoField(primary_key=True)', because + # that's assumed if it doesn't exist. + if att_name == 'id' and field_type == 'AutoField(' and extra_params == {'primary_key': True}: + continue + + # Add 'null' and 'blank', if the 'null_ok' flag was present in the + # table description. + if row[6]: # If it's NULL... + extra_params['blank'] = True + # Don't know why these are here, commenting out + #if not field_type in ('TextField(', 'CharField('): + extra_params['null'] = True + + field_desc = 'models.%s' % field_type + if extra_params: + if not field_desc.endswith('('): + field_desc += ', ' + field_desc += ', '.join(['%s=%r' % (k, v) for k, v in extra_params.items()]) + field_desc += ')' + if comment_notes: + field_desc += ' # ' + ' '.join(comment_notes) + # Set the dict key/value to the field name and the evaluated field description + fields[att_name] = eval(field_desc) + fields[att_name].name = att_name + return fields + + @classmethod + def get_field_type(cls, connection, table_name, row): + """ + Given the database connection, the table name, and the cursor row + description, this routine will return the given field type name, as + well as any additional keyword parameters and notes for the field. + """ + field_params = {} + field_notes = [] + + try: + field_type = connection.introspection.get_field_type(row[1], row) + except KeyError: + field_type = 'TextField' + field_notes.append('This field type is a guess.') + + # This is a hook for DATA_TYPES_REVERSE to return a tuple of + # (field_type, field_params_dict). + if type(field_type) is tuple: + field_type, new_params = field_type + field_params.update(new_params) + + # Add max_length for all CharFields. + if field_type == 'CharField' and row[3]: + field_params['max_length'] = row[3] + + if field_type == 'DecimalField': + # TODO These are reading the wrong values sometimes, returning 65535 for both. + # So minimize them + field_params['max_digits'] = min(row[4], 100) + field_params['decimal_places'] = min(row[5], 20) + + return field_type, field_params, field_notes + diff --git a/footprint/main/utils/query_parsing.py b/footprint/main/utils/query_parsing.py new file mode 100644 index 000000000..143755bb3 --- /dev/null +++ b/footprint/main/utils/query_parsing.py @@ -0,0 +1,250 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# model: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +from django.db.models import Q, Min +import sys +from django.contrib.gis.db.models.query import GeoQuerySet +from django.db.models.query import QuerySet +from django.template.defaultfilters import slugify +from footprint.main.lib.functions import deep_map_dict_structure, to_list, map_to_dict +from footprint.main.models.geospatial.feature_class_creator import FeatureClassCreator +from footprint.main.utils.dynamic_subclassing import resolve_field, resolve_field_of_type, resolve_queryable_name_of_type +from footprint.main.utils.utils import full_module_path, clear_many_cache + +COMPARISON_LOOKUP = { + '<': 'lt', + '<=': 'lte', + '>': 'gt', + '>=': 'gte', + 'BEGINS_WITH': 'startswith', + 'ENDS_WITH': 'endswith', + 'CONTAINS': 'contains' # TODO would be 'in' for SC Arrays + #'MATCHES': + #'ANY': + #TYPE_IS - +} + +def parse_query(config_entity, manager, filters=None, joins=None, aggregates=None, group_bys=None): + queryset = manager + group_by_values = None + annotation_tuples = None + + # Make sure all related models have been created before querying + FeatureClassCreator(config_entity).ensure_dynamic_models() + + # Any joins are db_entity_keys and resolve to feature classes of the config_entity + related_models = map(lambda join: config_entity.feature_class_of_db_entity_key(join), joins or []) + + # Use group_by_values to group by and then attach the aggregates to each unique result via annotation + # If aggregates are specified but group by is not, we use aggregate to just get a single result + # For now we assume any filtering should be applied BEFORE aggregation + if filters: + queryset = queryset.filter(parse_token(filters, manager, related_models)) + + # We only need to join explicitly if the join is not included in one of the group by fields + manual_joins = joins or [] if not group_bys else \ + set(joins or [])-\ + set(map(lambda group_by: resolve_db_entity_key_of_field_path(parse_group_by(group_by, manager, related_models), manager, related_models), group_bys)) + + if manual_joins: + # If there are joins, filter the queryset by inner join on the related_model pk through geography + for related_model in related_models: + queryset = queryset.filter(**{'{0}__isnull'.format(resolve_field_path_via_geographies('pk', manager, [related_model])):False}) + + # If there are aggregates, they are either part of the main table or join table + if aggregates: + # Resolve the field path using available joins via geographies or on the main model + # Then send the resovled field + annotation_tuples = map( + lambda aggregate: parse_annotation(aggregate, manager, related_models), + aggregates) + if group_bys: + group_by_values = map( + lambda group_by: parse_group_by(group_by, manager, related_models), + to_list(group_bys)) + + if group_by_values: + queryset = queryset.values(*group_by_values).order_by(*group_by_values) + if annotation_tuples: + if not group_by_values: + queryset = [queryset.aggregate(*map(lambda annotation_tuple: annotation_tuple[0](annotation_tuple[1]), annotation_tuples))] + else: + for annotation_tuple in annotation_tuples: + if isinstance(annotation_tuple[0], basestring): + # Chain named queryset methods + queryset = getattr(queryset, annotation_tuple[0])(annotation_tuple[1]) + else: + # Annotation built-in functions + queryset = queryset.annotate(annotation_tuple[0](annotation_tuple[1])) + + return queryset + +def is_aggregate(annotation_function): + #TODO + return False + +def resolve_field_path_via_geographies(field_path, manager, related_models): + """ + Resolve the given field path in case its not absolute. + For instance, if it is 'block' and one of our related models accessible via geographies__relatedmodel has that property, + return 'geographies__relatedmodel__block' + It will also be tested against the main manager after all related models fail, + e.g. manager.values(field_path) if successful would simply return field_path + :param field_path: django field path. e.g. du or built_form__name + :param manager: The main manager by which the related models are resolved and by which the full path is computed + :param related_models: models joined to the manager. For instance. manager.model is BaseFeature, a related_model could be + CensusBlock, which might be related to the former via 'geographies__censusblock9rel'. The relationship is computed + by assuming that the related model is related by geographies and looking for a field matching its type + :return: + """ + geography_class = manager.model.geographies.field.rel.to + for model in related_models: + try: + # See if the field_name resolves + # There's probably a more efficient way to do this + model.objects.values(field_path) + except: + pass + else: + # Success, find the path to this model from geographies + geography_related_field_name = resolve_queryable_name_of_type(geography_class, model) + return 'geographies__%s__%s' % (geography_related_field_name, field_path) + try: + if field_path.split('__')[0] == manager.model.db_entity_key: + # If the manager model db_entity_key was used in the path, just strip it out + updated_field_path = '__'.join(field_path.split('__')[1:]) + manager.values(updated_field_path) + else: + # Otherwise test query with the full path + updated_field_path = field_path + manager.values(updated_field_path) + # Success, return the field_path + return updated_field_path + except: + raise Exception("Cannot resolve field path %s to the main model %s or any joined models %s" % + (field_path, manager.model, related_models)) + +def resolve_db_entity_key_of_field_path(field_path, manager, related_models): + + for related_model in related_models: + try: + # See if the field_name resolves + # There's probably a more efficient way to do this + related_model.objects.values(field_path) + # Success, return the db_entity_key of the related_models + return related_model.db_entity_key + except: + pass + try: + # If an absolute path, take the first segment and try to resolve it as a db_entity_key + first_segment = field_path.split('__') + try: + if manager.model.config_entity.computed_db_entities(key=first_segment).count() > 0: + return first_segment + except: + # Must be a main feature class field_path + return manager.model.config_entity.db_entity_key_of_feature_class(manager.model) + except: + raise Exception("Cannot resolve field path %s to the main model %s or any joined models %s" % + (field_path, manager.model, related_models)) + + +def parse_group_by(group_by_token, manager, related_models): + """ + :param group_by_token: + :return: + """ + return parse_simple_token(group_by_token, manager, related_models) + +def parse_annotation(aggregate_token, manager, related_models): + """ + token in the form {rightSide: {tokenValue: field_name}, tokenValue: 'AVG|SUM|COUNT'} + :param aggregate_token: + :param manager: main Manager + :param related_models: related models + :return: the aggregate function to be passed to annotate() along with any others + """ + function_or_name = resolve_annotation(manager, aggregate_token['tokenValue']) if aggregate_token.get('tokenValue', None) else None + field = aggregate_token['rightSide']['tokenValue'] if 'rightSide' in aggregate_token else aggregate_token['tokenValue'] + resolved_field_path = resolve_field_path_via_geographies( + field, + manager, + (related_models or [])) + return (function_or_name, resolved_field_path) if function_or_name else resolved_field_path + +def parse_token(token, manager, related_models): + + token_type = token['tokenType'] + left_side = token.get('leftSide', None) + right_side = token.get('rightSide', None) + + if token_type == 'AND': + left_side_result = parse_token(left_side, manager, related_models) + right_side_result = parse_token(right_side, manager, related_models) + return left_side_result & right_side_result + elif token_type == 'OR': + left_side_result = parse_token(left_side, manager, related_models) + right_side_result = parse_token(right_side, manager, related_models) + return left_side_result | right_side_result + # TODO handle NOT with ~Q() + elif token_type in COMPARISON_LOOKUP.keys(): + return Q(**{'{0}__{1}'.format(parse_simple_token(left_side, manager, related_models), COMPARISON_LOOKUP[token_type]): + parse_simple_token(right_side, manager, related_models)}) + elif token_type == '=': + return Q(**{parse_simple_token(left_side, manager, related_models): + parse_simple_token(right_side, manager, related_models)}) + elif token_type == '!=': + return Q(**{parse_simple_token(left_side, manager, related_models): + parse_simple_token(right_side, manager, related_models)}) + # TODO handle other operators + return {} + +def parse_simple_token(token, manager, related_models): + if token['tokenType'] == 'PROPERTY': + # Resolve the field path in case it's relative to a joined related_model + return resolve_field_path_via_geographies('__'.join(token['tokenValue'].split('.')), manager, related_models) + elif token['tokenType'] == 'NUMBER': + return float(token['tokenValue']) + elif token['tokenType'] == 'STRING': + return token['tokenValue'] + # TODO handle booleans and other types + return token['tokenType'] + +def resolve_annotation(manager, annotation): + class_name = annotation.lower().capitalize() + if hasattr(sys.modules['django.db.models'], class_name): + return getattr(sys.modules['django.db.models'], class_name) + function_name = slugify(annotation.lower()) + if hasattr(manager, function_name): + return function_name + +def annotated_related_feature_class_pk_via_geographies(manager, config_entity, db_entity_keys): + """ + To join a related model by geographic join + """ + # Success, find the path to this model from geographies + geography_class = manager.model.geographies.field.rel.to + def resolve_related_model_pk(db_entity_key): + related_model = config_entity.feature_class_of_db_entity_key(db_entity_key) + try: + geography_related_field_name = resolve_queryable_name_of_type(geography_class, related_model) + except: + # Sometimes the geography class hasn't had its fields cached properly. Fix here + clear_many_cache(geography_class) + geography_related_field_name = resolve_queryable_name_of_type(geography_class, related_model) + + return 'geographies__%s__pk' % (geography_related_field_name) + + pk_paths = map_to_dict(lambda db_entity_key: + [db_entity_key, Min(resolve_related_model_pk(db_entity_key))], + db_entity_keys) + + return manager.annotate(**pk_paths) diff --git a/footprint/main/utils/range.py b/footprint/main/utils/range.py new file mode 100644 index 000000000..b0e850833 --- /dev/null +++ b/footprint/main/utils/range.py @@ -0,0 +1,59 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +__author__ = 'calthorpe_associates' + +class Range: + def __init__(self, start, end): + self.start = start + self.end = end + + def length(self): + return self.end - self.start + + def overlaps(self, other): + return not(self.end < other.start or other.end < self.start) + + def name(self): + return '_'.join([str(self.start), str(self.end)]) + + def __unicode__(self): + return u'%s' % self.name() + + def __str__(self): + return self.__unicode__().encode('utf-8') + + +def make_ranges(min, max, count, explicit_increments=[]): + full_range = max-min + increment = full_range/count + if len(explicit_increments) > 0: + if len(explicit_increments)+1 != count: + raise Exception("explicit_increments count ({0}) is not one less than count ({1})".format(len(explicit_increments), count)) + all_increments = [min]+explicit_increments+[max] + return map(lambda index: Range(all_increments[index], all_increments[index+1]), range(len(all_increments)-1)) + else: + return map(lambda index: Range(min+increment*index, min+increment*(index+1)), range(count)) + +# Complements make_ranges by creating a sequences of values between and including min and max +def make_increments(min, max, count): + full_range = max-min + # Decrease the count so that our last increment is max + increment = full_range/count-1 + # Creates a sequence starting a min and ending at max, with intermediates equidistant + return map(lambda index: min+increment*index, range(count)) diff --git a/footprint/main/utils/subclasses.py b/footprint/main/utils/subclasses.py new file mode 100644 index 000000000..6d3ab8f5d --- /dev/null +++ b/footprint/main/utils/subclasses.py @@ -0,0 +1,71 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +from footprint.main.lib.functions import unique + +__author__ = 'calthorpe_associates' + +def get_subclasses(classes, level=0): + """ + Return the list of all subclasses given class (or list of classes) has. + Inspired by this question: + http://stackoverflow.com/questions/3862310/how-can-i-find-all-subclasses-of-a-given-class-in-python + """ + # for convenience, only one class can can be accepted as argument + # converting to list if this is the case + if not isinstance(classes, list): + classes = [classes] + + if level < len(classes): + classes += classes[level].__subclasses__() + return get_subclasses(classes, level+1) + else: + return classes + +def match_subclasses(c, matching_lambda=lambda x: True): + """ + Recursively find the subclasses matching lambda + :param c: starting class + :param matching_lambda: filter function that takes each subclass and returns true or false. Unmatched classes + are still recursed beforehand. Defaults to returning all. + :return: + """ + subclasses = c.__subclasses__() + for d in list(subclasses): + subclasses.extend(get_subclasses(d)) + return filter(matching_lambda, unique(subclasses)) + +def receiver_subclasses(signal, sender, dispatch_uid_prefix, **kwargs): + """ + A decorator for connecting receivers and all receiver's subclasses to signals. Used by passing in the + signal and keyword arguments to connect:: + + @receiver_subclasses(post_save, sender=MyModel) + def signal_receiver(sender, **kwargs)G: + ... + """ + def _decorator(func): + all_senders = get_subclasses(sender) + for snd in all_senders: + signal.connect(func, sender=snd, dispatch_uid=dispatch_uid_prefix+'_'+snd.__name__, **kwargs) + return func + return _decorator + +def connect_subclasses(func, signal, sender, dispatch_uid_prefix, **kwargs): + all_senders = get_subclasses(sender) + for snd in all_senders: + signal.connect(func, sender=snd, dispatch_uid=dispatch_uid_prefix+'_'+snd.__name__, **kwargs) diff --git a/footprint/main/utils/track_refs.py b/footprint/main/utils/track_refs.py new file mode 100644 index 000000000..497480574 --- /dev/null +++ b/footprint/main/utils/track_refs.py @@ -0,0 +1,57 @@ +import logging +import sys + +__author__ = 'calthorpe' +logger = logging.getLogger(__name__) + +class TrackRefs: + """Object to track reference counts across test runs.""" + + def __init__(self, limit=40): + self.type2count = {} + self.type2all = {} + self.limit = limit + + def update(self): + obs = sys.getobjects(0) + type2count = {} + type2all = {} + for o in obs: + all = sys.getrefcount(o) + + if type(o) is str and o == '': + # avoid dictionary madness + continue + t = type(o) + if t in type2count: + type2count[t] += 1 + type2all[t] += all + else: + type2count[t] = 1 + type2all[t] = all + + ct = [(type2count[t] - self.type2count.get(t, 0), + type2all[t] - self.type2all.get(t, 0), + t) + for t in type2count.iterkeys()] + ct.sort() + ct.reverse() + printed = False + + logger.debug("----------------------") + logger.debug("Memory profiling") + i = 0 + for delta1, delta2, t in ct: + if delta1 or delta2: + if not printed: + logger.debug("%-55s %8s %8s" % ('', 'insts', 'refs')) + printed = True + + logger.debug("%-55s %8d %8d" % (t, delta1, delta2)) + + i += 1 + if i >= self.limit: + break + + self.type2count = type2count + self.type2all = type2all \ No newline at end of file diff --git a/footprint/main/utils/uf_toolbox.py b/footprint/main/utils/uf_toolbox.py new file mode 100644 index 000000000..50018668b --- /dev/null +++ b/footprint/main/utils/uf_toolbox.py @@ -0,0 +1,476 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import string +import subprocess, threading, Queue +#from calthorpe.main.lib.functions import merge +from django.db import transaction +import psycopg2 +#from calthorpe.main.utils.utils import database_settings + + +##----------------------------------------------- +from footprint import settings +from footprint.common.utils.postgres_utils import pg_connection_parameters + + +class MultithreadProcess(threading.Thread): + """Threaded Unit of work""" + + def __init__(self, queue, sql_job_string): + threading.Thread.__init__(self) + self.queue = queue + self.sql_job_string = sql_job_string + + def run(self): + while True: + #grabs host from queue + job = self.queue.get() + Task = self.sql_job_string.format(job['start_id'], job['end_id']) + execute_sql(Task) + self.queue.task_done() + return + + +def queue_process(): + queue = Queue.Queue() + return queue + +def execute_sql(pSQL): + + + try: + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + curs = conn.cursor() + except Exception, E: + print str(E) + + + + try: + curs.execute(pSQL) + except Exception, E: + print str(E) + raise Exception('SQL: {0}. Original Message: {1}'.format(pSQL, E.message)) + finally: + conn.commit() + curs.close() + + +def copy_from_text_to_db(text_file, table_name): + try: + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + curs = conn.cursor() + + except Exception, E: + print str(E) + + + try: + curs.copy_from(text_file, table_name) + except Exception, E: + print str(E) + raise Exception('Original Message: {0}'.format(E.message)) + finally: + conn.commit() + curs.close() + + +def report_sql_values(pSQL, fetch_type): + with transaction.commit_manually(): + try: + conn = psycopg2.connect(**pg_connection_parameters(settings.DATABASES['default'])) + conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT) + curs = conn.cursor() + except Exception, E: + print str(E) + + try: + curs.execute(pSQL) + except Exception, E: + print str(E) + + Sql_Values = getattr(curs, fetch_type)() + conn.commit() + curs.close() + return Sql_Values + + +def report_sql_values_as_dict(query, connection_string): + try: + connection = psycopg2.connect(connection_string) + except Exception, E: + print str(E) + curs = connection.cursor() + + try: + curs.execute(query) + except Exception, E: + print str(E) + + r = [dict((curs.description[i][0], value) for i, value in enumerate(row)) for row in curs.fetchall()] + curs.connection.close() + curs.close() + return r if r else None + + +def copy_to_psql(output_tmp, cDSN, working_schema, tmp_table_name): + try: + conn = psycopg2.connect(cDSN) + except Exception, E: + print str(E) + curs = conn.cursor() + + curs.copy_from(output_tmp, working_schema + "." + tmp_table_name) + conn.commit() + curs.close() + +def connect(conn_string): + '''given a connection string, connects to the database and returns a cursor ''' + try: + gDB = psycopg2.connect(conn_string) + except Exception, E: + print str(E) + raise + return gDB + + +def get_conn_string(db_name): + d = database_settings(db_name) + return 'dbname=' + d['NAME'] + ' host=' + d['HOST'] + ' user=' + d['USER'] + ' password=' + d['PASSWORD'] + + +def db_table_exists(table, cursor=None): + try: + if not cursor: + from django.db import connection + + cursor = connection.cursor() + if not cursor: + raise Exception + try: + selection = """SELECT tablename FROM pg_tables""" + cursor.execute("SELECT tablename FROM pg_tables") + except: + raise + table_names = cursor.fetchall() + tables = [] + for t in table_names: + tables.append(t[0]) + # table_names = connection.introspection.get_table_list(cursor) + except: + raise Exception("unable to determine if the table '%s' exists" % table) + else: + return table in tables + + +def executeSQL_now(conn_string, sqls, db=None, **kwargs): + resultset = [] + for sql in sqls: + try: + db = connect(conn_string) + gCurs = db.cursor() + + try: + gCurs.execute(sql) + except Exception, E: + print str(E) + raise + + try: + result = gCurs.fetchall() + except: + result = () + gCurs.close() + db.commit() + + results = [] + for n in result: results.append(n) + resultset.append(results) + + except Exception, E: + try: + gCurs.close() + except: + pass + resultset.append(E) + + return resultset + + +def get_geom_reg(conn_string, schema, table): + #TODO: update this to use the views.geometry columns + query = "select srid, type, coord_dimension from views.geometry_columns " \ + "where f_table_schema = '{0}' and f_table_name = '{1}'".format(schema, table) + return executeSQL_now(conn_string, [query])[0][0] + + +def list_all_geom_tables(conn_string): + query = "select table_schema, table_name from information_schema.columns where column_name = 'wkb_geometry';" + return executeSQL_now(conn_string, [query]) + + +def get_constraints(conn_string, schema, table): + query = """select constraint_name from information_schema.constraint_column_usage + where table_schema = '{0}' and table_name = '{1}'""".format(schema, table) + return executeSQL_now(conn_string, [query]) + +# return qualities of the geometry field + +def get_geom_type(conn_string, schema, table): + query = "select distinct(geometrytype(wkb_geometry)) from {0}.{1}".format(schema, table) + return executeSQL_now(conn_string, [query]) + + +def get_geom_SRID(conn_string, schema, table): + query = "select distinct(st_srid(wkb_geometry)) from {0}.{1}".format(schema, table) + return executeSQL_now(conn_string, [query]) + + +def get_geom_dims(conn_string, schema, table): + query = "select distinct(ST_CoordDim(wkb_geometry)) from {0}.{1}".format(schema, table) + return executeSQL_now(conn_string, [query]) + +# add constraints functions + +def add_constraint_geom_type(conn_string, schema, table, geom_type): + add_constraint = "ALTER TABLE {0}.{1} ADD CONSTRAINT enforce_geotype_wkb_geometry check(geometrytype(wkb_geometry) = '{2}');".format( + schema, table, geom_type) + executeSQL_now(conn_string, [add_constraint]) + + +def add_constraint_SRID(schema, table, srid): + add_constraint = "ALTER TABLE {0}.{1} ADD CONSTRAINT enforce_srid_wkb_geometry check (st_srid(wkb_geometry) = '{2}');".format( + schema, table, srid) + execute_sql(add_constraint) + + +def add_constraint_geom_dims(conn_string, schema, table, geom_dims): + add_constraint = "ALTER TABLE {0}.{1} ADD CONSTRAINT enforce_dims_wkb_geometry check (ST_CoordDim(wkb_geometry) = {2});".format( + schema, table, geom_dims) + executeSQL_now(conn_string, [add_constraint]) + +# drop a constraint +def drop_spatial_constraint(schema, table, type, column): + query = 'ALTER TABLE {0}.{1} DROP CONSTRAINT enforce_{2}_{3};'.format(schema, table, type, column) + execute_sql(query) + +# add geometric index +def add_geom_idx(schema, table, column="wkb_geometry"): + query = 'CREATE INDEX {0}_geom_idx on {1}.{0} using GIST ({2});'.format(table, schema, column) + execute_sql(query) + + +def drop_geom_idx(schema, table): + query = 'DROP INDEX {0}."{1}_wkb_geometry_id";'.format(schema, table) + execute_sql(query) + + +def add_attribute_idx(conn_string, schema, table, field): + query = 'create index {1}_{2}_idx on {0}.{1} ({2});'.format(schema, table, field) + executeSQL_now(conn_string, [query]) + + +def add_primary_key(schema, table, field): + query = 'alter table {0}.{1} add constraint {1}_pkey primary key ({2});'.format(schema, table, field) + execute_sql(query) + + +def drop_table(table_name): + pSql = '''drop table if exists {0} cascade;'''.format(table_name) + execute_sql(pSql) + +def truncate_table(table_name): + pSql = '''truncate table {0};'''.format(table_name) + execute_sql(pSql) + +#---------------------------------------------------------------------------------------- +def count_cores(): + cores = 0 + + for line in open('/proc/cpuinfo','r'): + if 0 == string.find(line, "processor"): + cores = cores + 1 + + return cores + + +def create_sql_calculations(table_fields, sql_format): + sql_calculations = '' + for field in table_fields: + sql_calculations += sql_format.format(field) + + return sql_calculations + + +def create_sql_calculations_two_variables(table_fields, sql_query): + sql_calculations = '' + for field in table_fields: + sql_calculations += sql_query.format(field[0], field[1]) + return sql_calculations + + +def create_sql_calculations_four_variables(table_fields, sql_query): + sql_calculations = '' + for field in table_fields: + sql_calculations += sql_query.format(field[0], field[1], field[2], field[3], field[4]) + return sql_calculations + + +# make sure all spatial tables in db have proper constraints +# this will ensure that they are registered in the geometry_columns view +def validate_constraints_whole_db(conn_string): + geom_tables = list_all_geom_tables(conn_string) + for t in geom_tables[0]: + schema, table = t[0], t[1] + validate_constraints(conn_string, schema, table) + + +def validate_constraints(conn_string, schema, table): + info = schema + '.' + table + constraints = get_constraints(conn_string, schema, table)[0] + + # make sure the SRID has a constraint, and if not, create one + if ("enforce_srid_wkb_geometry",) not in constraints: + try: + srid = get_geom_SRID(conn_string, schema, table)[0][0] + # TODO: better error handling here -- can we try fixing the problem before rejecting it? + if len(srid) > 1: + print "there are multiple SRID's for this table, cannot add constraint" + raise + elif len(srid) == 0: + print "there is no SRID for the geometry in this table. cannot add constraint" + raise + else: + add_constraint_SRID(conn_string, schema, table, srid[0]) + info += ': added SRID constraint, ' + except Exception, E: + print E + raise + else: + info += ': SRID OK, ' + + # make sure the geometry type has a constraint, and if not, create one + if ("enforce_geotype_wkb_geometry",) not in constraints: + try: + geom_type = get_geom_type(conn_string, schema, table)[0][0] + # TODO: better error handling here -- can we try fixing the problem before rejecting it? + if len(geom_type) > 1: + print "there are multiple geometry types for this table. cannot add constraint" + raise + elif len(geom_type) == 0: + print "there is no geometry type for the geometry in this table. cannot add constraint" + raise + else: + add_constraint_geom_type(conn_string, schema, table, geom_type[0]) + info += ' added geom_type constraint, ' + except Exception, E: + print E; + raise + + else: + info += ' geom_type OK, ' + + # make sure the number of dimensions has a constraint, and if not, create one + if ("enforce_dims_wkb_geometry",) not in constraints: + try: + dims = get_geom_dims(conn_string, schema, table)[0][0] + # TODO: better error handling here -- can we try fixing the problem before rejecting it? + if len(dims) > 1: + print "there are inconsistent coordinate dimensions for this table. cannot add constraint" + raise + elif len(dims) == 0: + print "there are no coordinate dimensions for the geometry in this table. cannot add constraint" + raise + else: + add_constraint_geom_dims(conn_string, schema, table, dims[0]) + info += ' added coord_dims constraint' + except Exception, E: + print E + raise + + else: + info += ' coord_dims OK ' + + print info + + +def reproject_table(schema, table, srid): + + reproject = 'UPDATE {0}.{1} set wkb_geometry = ST_setSRID(ST_transform(wkb_geometry, {2}),{2});' \ + .format(schema, table, srid) + execute_sql(reproject) + + +def ogr_to_gdb(conn_string, schema, table, output_gdb): + reproject_table(conn_string, schema, table, '4326') + ogr_command = 'ogr2ogr -overwrite -skipfailures -f "FileGDB" "{1}" PG:"{0}" "{2}.{3}" ' \ + .format(conn_string, output_gdb, schema, table) + print ogr_command + subprocess.call(ogr_command, shell=True) + + +def register_geometry_columns(server, schema=None): + select_tables = "Truncate geometry_columns cascade; \n" \ + "select table_name, table_schema from information_schema.columns " \ + "where column_name = 'wkb_geometry' " \ + "and table_schema = '{0}';".format(schema) + if not schema: + select_tables = "Truncate geometry_columns cascade; \n" \ + "select table_name, table_schema from information_schema.columns " \ + "where column_name = 'wkb_geometry';" + + try: + gDB = psycopg2.connect(server) + except Exception, E: + print str(E) + raise + gCurs = gDB.cursor() + + try: + print select_tables + gCurs.execute(select_tables) + except Exception, E: + print str(E) + raise + tables_to_fix = gCurs.fetchall() + print tables_to_fix + if not schema: + print "assigning schema" + + for t in tables_to_fix: + print t + update = ''' + + INSERT INTO geometry_columns(f_table_catalog, f_table_schema, f_table_name, f_geometry_column, coord_dimension, + srid, "type") + SELECT '', '{0}', '{1}', 'wkb_geometry', ST_CoordDim(wkb_geometry), ST_SRID(wkb_geometry), + ST_GeometryType(wkb_geometry) + FROM {0}.{1} LIMIT 1;'''.format(t[1], t[0]) + try: + print update + r = gCurs.execute(update) + gDB.commit() + except Exception, E: + print str(E) + gDB.close() + gDB = psycopg2.connect(server) + gCurs = gDB.cursor() + + diff --git a/footprint/main/utils/utils.py b/footprint/main/utils/utils.py new file mode 100644 index 000000000..8902159ba --- /dev/null +++ b/footprint/main/utils/utils.py @@ -0,0 +1,664 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import inspect + +from io import open +import re +from django.core.exceptions import ImproperlyConfigured +from django.db.models import Manager, get_model +import datetime +from django.db.models.fields.related import ReverseManyRelatedObjectsDescriptor +import os +from subprocess import Popen, PIPE, STDOUT +from django.db import connections +from os import path +import sys +from sarge import capture_both +from footprint.main.lib.functions import merge, map_to_dict, flatten +from django.conf import settings +from shapely.geometry import LineString +from django.contrib.gis.geos import MultiPolygon, Polygon, LinearRing +import pwd + +def os_user(): + for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'): + user = os.environ.get(name) + if user: + return user + + # If not user from os.environ.get() + return pwd.getpwuid(os.getuid())[0] + +def decimal_constant_factory(value): + return lambda: 0.0000000000 + +def import_json_file(path): + return open(path).read().replace('\n', '').replace('\t', '') + +def resolve_model(class_path): + """ + Resolves a class path to a Django model class + :param class_path: a string model class path + :return: + """ + return get_model(*class_path.split('.', 1)) + +def resolvable_model_name(cls): + """ + Reverse of resolve_model. Returns the model cls as an app name plus class name + """ + return '%s.%s' % (cls._meta.app_label, cls.__name__) + +def resolve_module_attr(str): + """ + Resolves any module attr, a class, function, whatever from a str + :param str: a complete path to an attribute + return the class, function, etc, or throws an AttributeError if not found + """ + parts = str.split('.') + module = '.'.join(parts[0:-1]) + attr = parts[-1] + try: + return getattr(sys.modules[module], attr) + except KeyError, e: + raise Exception("parts: {parts}, module: {module}, attr:{attr}. Original exception: {e}".format(parts=parts, module=module, attr=attr, e=e)) + +def full_module_path(cls_or_func): + """ + Return the full module path of a class (or anything with a __name__) plus the name so that resolve_module_attr can restore it later + """ + return '%s.%s' % (sys.modules[cls_or_func.__module__].__name__, cls_or_func.__name__) + +def resolvable_module_attr_path(file_name, cls_or_attr_name): + """ + Return the full module path of a class or other attr (function, constant, etc) plus its name so that resolve_module_attr can restore it later + """ + return '%s.%s' % (sys.modules[file_name].__name__, cls_or_attr_name) + + +def postgres_url_to_connection_dict(url): + try: + return re.match('postgres://(?P.+?):(?P.+?)/(?P.+?):(?P.*?)/(?P.+)', url).groupdict() + except Exception, e: + raise e + +def file_url_to_path(url): + try: + return re.match('file://(?P.+)', url).groupdict()['path'] + except Exception, e: + raise e + +def getSRIDForTable(db, table_name): + cur = connections[db].cursor() + sql = 'select st_srid(wkb_geometry) from ' + table_name + ' LIMIT 1' + cur.execute(sql) + result = cur.fetchall() + cur.close() + return result[0][0] + +def parse_schema_and_table(full_table_name): + """ + Returns the database schema and table by parsing a full table name of the form "schema"."table" + """ + return map(lambda str: strip_quotes(str), full_table_name.split('.')) + + +def strip_quotes(str): + return str[1:-1] if str[0] == '"' else str + + +def table_name_only(dynamic_class): + return dynamic_class._meta.db_table + + +def get_or_none(model, **kwargs): + try: + return model.objects.get(**kwargs) + except model.DoesNotExist: + return None + +def update_and_return_dict(dict1,dict2): + dict1.update(dict2) + return dict1 + +def get_or_none_from_queryset(queryset, **kwargs): + try: + return queryset.get(**kwargs) + except Exception, E: + return None + + +def timestamp(): + """returns a formatted timestamp with detail of the hour and minute" + """ + def make_character_string(time_unit): + return str(time_unit) if len(str(time_unit)) == 2 else "0{0}".format(time_unit) + + now = datetime.datetime.now() + time = dict( + year=now.year, + month=make_character_string(now.month), + day=make_character_string(now.day), + hour=make_character_string(now.hour), + minute=make_character_string(now.minute), + second=make_character_string(now.second) + ) + + timestamp = "{year}{month}{day}_{hour}{minute}".format(**time) + return timestamp + +## {{{ http://code.activestate.com/recipes/410469/ (r5) +class XmlListConfig(list): + def __init__(self, aList): + for element in aList: + if element: + # treat like dict + if len(element) == 1 or element[0].tag != element[1].tag: + self.append(XmlDictConfig(element)) + # treat like list + elif element[0].tag == element[1].tag: + self.append(XmlListConfig(element)) + elif element.text: + text = element.text.strip() + if text: + self.append(text) + + +class XmlDictConfig(dict): + ''' + Example usage: + + >>> tree = ElementTree.parse('your_file.xml') + >>> root = tree.getroot() + >>> xmldict = XmlDictConfig(root) + + Or, if you want to use an XML string: + + >>> root = ElementTree.XML(xml_string) + >>> xmldict = XmlDictConfig(root) + + And then use xmldict for what it is... a dict. + ''' + + def __init__(self, parent_element): + childrenNames = [] + for child in parent_element.getchildren(): + childrenNames.append(child.tag) + + if parent_element.items(): #attributes + self.update(dict(parent_element.items())) + for element in parent_element: + if element: + # treat like dict - we assume that if the first two tags + # in a series are different, then they are all different. + #print len(element), element[0].tag, element[1].tag + if len(element) == 1 or element[0].tag != element[1].tag: + aDict = XmlDictConfig(element) + # treat like list - we assume that if the first two tags + # in a series are the same, then the rest are the same. + else: + # here, we put the list in dictionary; the key is the + # tag name the list elements all share in common, and + # the value is the list itself + aDict = {element[0].tag: XmlListConfig(element)} + # if the tag has attributes, add those to the dict + if element.items(): + aDict.update(dict(element.items())) + + if childrenNames.count(element.tag) > 1: + try: + currentValue = self[element.tag] + currentValue.append(aDict) + self.update({element.tag: currentValue}) + except: #the first of its kind, an empty list must be created + self.update({element.tag: [aDict]}) #aDict is written in [], i.e. it will be a list + + else: + self.update({element.tag: aDict}) + # this assumes that if you've got an attribute in a tag, + # you won't be having any text. This may or may not be a + # good idea -- time will tell. It works for the way we are + # currently doing XML configuration files... + elif element.items(): + self.update({element.tag: dict(element.items())}) + # finally, if there are no child tags and no attributes, extract + # the text + else: + self.update({element.tag: element.text}) + +## end of http://code.activestate.com/recipes/410469/ }}} + +def create_media_subdir(relative_path): + subdir = path.join(settings.MEDIA_ROOT, relative_path) + if not os.path.exists(subdir): + os.makedirs(subdir) + + +def create_static_content_subdir(relative_path): + subdir = path.join(settings.STATIC_ROOT, relative_path) + if not os.path.exists(subdir): + os.makedirs(subdir) + + +def save_media_file(output_file, file_content): + #work around for Django bug where ContentFile does not support unicode + outputfilename = path.join(settings.MEDIA_ROOT, output_file) + f = open(outputfilename, "w") + f.write(file_content) + f.close() + return outputfilename + + +def string_not_empty(str, default): + return str if str != None and str != '' and str != u'' else default + + +def execute(command_and_args): + p = Popen(command_and_args, stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=False) + return p.communicate() + + +def execute_with_stdin(command_and_args, stdin): + """ + Executes a system command that requires input given to STDIN, such as psql + Returns a tuple (stdout, stderr) + """ + return capture_both(command_and_args + ' ' + stdin) + + +def execute_piped_with_stdin(commands_with_args, stdin): + """ + Executes a system command piped to another system command where one or both require input from STDIN, such as pg_dump | psql + Returns a tuple (stdout, stderr) + """ + p1 = Popen(commands_with_args[0], stdout=PIPE) + p2 = p1 + for command_with_args in commands_with_args[1:]: + p2 = Popen(command_with_args, stdin=p2.stdout, stdout=PIPE) + p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits. + return p2.communicate(input="\n".join(stdin)) + + +def load_template_source(path): + # TODO this should work for any templates dir + with open("{0}/{1}/{2}".format(settings.ROOT_PATH, 'main/templates', path), 'r') as f: + return f.read() + + +def database_settings(db): + connection = connections[db] + return connection.settings_dict + + +def connection_dict(db): + database = database_settings(db) + return dict( + host=database['HOST'], + dbname=database['NAME'], + user=database['USER'], + password=database['PASSWORD'], + port=5432 + ) + + +def database_connection_string(db): + settings = database_settings(db) + return "db_name=%s host=%s user=%s password=%s" % ( + settings['NAME'], settings['HOST'], settings['USER'], settings['PASSWORD']) + + +def database_connection_string_for_pys(db): + settings = database_settings(db) + return "dbname=%s host=%s user=%s password=%s" % ( + settings['NAME'], settings['HOST'], settings['USER'], settings['PASSWORD']) + + +def database_connection_string_for_ogr(db): + settings = database_settings(db) + return "dbname=\'%s\' host=\'%s\' port=\'%s\' user=\'%s\' password=\'%s\' " % ( + settings['NAME'], settings['HOST'], settings['PORT'], settings['USER'], settings['PASSWORD'] + ) + + +def to_tuple(point): + """ + Convert the Shapely class to a tuple for use by GeoDjango. + TODO figure out why GeoDjango interpolate methods don't exist + :param point: + :return: + """ + return point.x, point.y + + +def chop_geom(multipolygon, fraction): + """ + Transforms each point fraction the distance to the geometry's centroid to form a smaller geometry + :param geom: + :return: a multipolygon reduced by the fraction from the original + """ + + def transform_polygon(polygon): + def transform_linear_ring(linear_ring): + centroid = polygon.centroid + return LinearRing( + map(lambda point: to_tuple(LineString((point, centroid)).interpolate(fraction, normalized=True)), + linear_ring)) + + linear_rings = map(lambda linear_ring: transform_linear_ring(linear_ring), polygon) + if len(linear_rings) > 1: + return Polygon(linear_rings[0], [linear_rings[1:]]) + else: + return Polygon(linear_rings[0], []) + + return MultiPolygon(map(lambda polygon: transform_polygon(polygon), multipolygon)) + + +def has_explicit_through_class(instance, attribute): + """ + Returns through if this Many attribute has an explicit Through class + :param instance: The instance or class containing the attribute + :param attribute: A string representing the attribute + :return: True if an explicit through class exists, False otherwise + """ + field = getattr(instance, attribute) + if isinstance(field, ReverseManyRelatedObjectsDescriptor): + # If instance is a Model class + return not field.through._default_manager.__class__ == Manager + # Instance is a model instance + return not hasattr(field, 'add') + + +def foreign_key_field_of_related_class(model_class, related_model_class): + """ + For a model class, returns the foreign key ModelField of the given related_model_class. It's assumed that the model class doesn't define multiple foreign keys of the same type--that there is one foreign key for each of the two associated classes. related_model class can either match or be a subclass of the sought field rel.to class should + :param model_class: + :param related_model_class: The class or a subclass of the foreign key to match + :return: the ForeignKey Field of the given class_of_foreign_key + """ + fields = filter( + lambda field: field.rel and ( + field.rel.to == related_model_class or issubclass(related_model_class, field.rel.to)), + model_class._meta.fields) + if len(fields) == 1: + return fields[0] + else: + raise Exception( + "For through class {0}, expected exactly one field with to class {1}, but got {2}".format(model_class, + related_model_class, + len(fields))) + + +def resolve_attribute(instance, attribute_parts): + """ + Given attribute segments (perhaps created by splitting a django query attribute string (e.g. 'foo__id'), resolve the value of the attribute parts + :param instance: + :param attribute_parts: a list of string attribute + :return: whatever the attribute_parts resolve to by digging into the given instance + """ + return resolve_attribute( + getattr(instance, attribute_parts[0]) if hasattr(instance, attribute_parts[0]) else instance.get( + attribute_parts[0], None), + attribute_parts[1:]) if len(attribute_parts) > 0 else instance + +# From http://stackoverflow.com/questions/1165352/fast-comparison-between-two-python-dictionary +class DictDiffer(object): + """ + Calculate the difference between two dictionaries as: + (1) items added + (2) items removed + (3) keys same in both but changed values + (4) keys same in both and unchanged values + """ + + def __init__(self, current_dict, past_dict): + self.current_dict, self.past_dict = current_dict, past_dict + self.set_current, self.set_past = set(current_dict.keys()), set(past_dict.keys()) + self.intersect = self.set_current.intersection(self.set_past) + + def added(self): + return self.set_current - self.intersect + + def removed(self): + return self.set_past - self.intersect + + def changed(self): + return set(o for o in self.intersect if self.past_dict[o] != self.current_dict[o]) + + def unchanged(self): + return set(o for o in self.intersect if self.past_dict[o] == self.current_dict[o]) + + +def reduce_dict_to_difference(dct, comparison_dict, deep=True): + """ + Given a dict dct and a similar dict comparison dict, return a new dict that only contains the key/values of dct that are different than comparison dict, whether it's a key not in comparison_dict or a matching key with a different value. Specify deep=True to do a comparison of internal dicts + # TODO This could handle list comparison better for deep=True. Right now it just marks the lists as different if they are not equal + :param dct: + :param comparison_dict: + :param deep: Default True, compares embedded dictionaries by recursing + :return: A new dict containing the differences + """ + differ = DictDiffer(dct, comparison_dict) + return merge( + # Find keys and key values changed at the top level + map_to_dict(lambda key: [key, dct[key]], flatten([differ.added(), differ.changed()])), + # If deep==True recurse on dictionaries defined on the values + *map(lambda key: reduce_dict_to_difference(*map(lambda dictionary: dictionary[key], [dct, comparison_dict])), + # recurse on inner each dict pair + # Find just the keys with dict values + filter(lambda key: isinstance(dct[key], dict), differ.unchanged())) if deep else {} + ) + + +import pickle + + +def get_pickling_errors(obj, seen=None): + if seen == None: + seen = [] + try: + state = obj.__getstate__() + except AttributeError: + return + if state == None: + return + if isinstance(state, tuple): + if not isinstance(state[0], dict): + state = state[1] + else: + state = state[0].update(state[1]) + result = {} + for i in state: + try: + pickle.dumps(state[i], protocol=2) + except pickle.PicklingError: + if not state[i] in seen: + seen.append(state[i]) + result[i] = get_pickling_errors(state[i], seen) + return result + + +def call_if_function(obj, args): + """ + Takes an object and calls it as a function with *args if it is a function. Else returnes obj + :param obj: + :param args: + :return: + """ + return obj(*args) if hasattr(obj, '__call__') else obj + + +def expect(instance, *args): + """ + When initializing an instance, raise an ImproperlyConfigured exception if the given args are not set for the + given instance. Not set means None or not sepecified + :param instance: + :param args: + :return: + """ + missing_args = filter(lambda arg: not getattr(instance, arg), args) + if len(missing_args) > 0: + raise ImproperlyConfigured("Expected arg(s) {0}".format(', '.join(missing_args))) + + +def test_pickle(xThing,lTested = []): + import pickle + if id(xThing) in lTested: + return lTested + sType = type(xThing).__name__ + print('Testing {0}...'.format(sType)) + + if sType in ['type','int','str']: + print('...too easy') + return lTested + if sType == 'dict': + print('...testing members') + for k in xThing: + lTested = test_pickle(xThing[k],lTested) + print('...tested members') + return lTested + if sType == 'list': + print('...testing members') + for x in xThing: + lTested = test_pickle(x) + print('...tested members') + return lTested + + lTested.append(id(xThing)) + oClass = type(xThing) + + for s in dir(xThing): + if s.startswith('_'): + print('...skipping *private* thingy') + continue + #if it is an attribute: Skip it + try: + xClassAttribute = oClass.__getattribute__(oClass,s) + except AttributeError: + pass + else: + if type(xClassAttribute).__name__ == 'property': + print('...skipping property') + continue + + xAttribute = xThing.__getattribute__(s) + print('Testing {0}.{1} of type {2}'.format(sType,s,type(xAttribute).__name__)) + #if it is a function make sure it is stuck to the class... + if type(xAttribute).__name__ == 'function': + raise Exception('ERROR: found a function') + if type(xAttribute).__name__ == 'method': + print('...skipping method') + continue + if type(xAttribute).__name__ == 'HtmlElement': + continue + if type(xAttribute) == dict: + print('...testing dict values for {0}.{1}'.format(sType,s)) + for k in xAttribute: + lTested = test_pickle(xAttribute[k]) + continue + print('...finished testing dict values for {0}.{1}'.format(sType,s)) + + try: + oIter = xAttribute.__iter__() + except AttributeError: + pass + except AssertionError: + pass #lxml elements do this + else: + print('...testing iter values for {0}.{1} of type {2}'.format(sType,s,type(xAttribute).__name__)) + for x in xAttribute: + lTested = test_pickle(x,lTested) + print('...finished testing iter values for {0}.{1}'.format(sType,s)) + + try: + xAttribute.__dict__ + except AttributeError: + pass + else: + #this attribute should be explored seperately... + lTested = test_pickle(xAttribute,lTested) + continue + pickle.dumps(xAttribute) + + + print('Testing {0} as complete object'.format(sType)) + pickle.dumps(xThing) + return lTested + +def map_property_path(iterable, path): + """ + Sproutcore style function to map all items in iterable to the path given by path, which might be + dot-separated. Items of iterable can be objects or dicts + returns al list of results of the mapping, or None for items that fail to map to anything + """ + return map(lambda item: get_property_path(item, path), + iterable) + +def get_property_path(dict_or_obj, path): + """ + Sproutcore style get_path. Given a dictionary and a dot-separated path, Digs into the dictionary until + the path is resolved fully or a None value is encountered. + :param dict_or_obj: + :param path: + :return: + """ + segments = path.split('.') + value = dict_or_obj.get(segments[0], None) if \ + isinstance(dict_or_obj, dict) else \ + hasattr(dict_or_obj, segments[0]) and getattr(dict_or_obj, segments[0]) + if len(segments) == 1 or not value: + return value + else: + return get_property_path(value, '.'.join(segments[1:])) + + +def get_one_or(list, value): + """ + Simply return the only element in the list or default to the given value + """ + if len(list) == 1: + return list[0] + if len(list) == 0: + return value + raise Exception("List had more than one value: %s" % list) + + +def clear_many_cache_on_instance_field(many_field): + """ + Call clear_many_cache on an instance many field + :param many_field: + :return: + """ + model = super(many_field.__class__, many_field).get_query_set().model + clear_many_cache(model) + + +def clear_many_cache(model): + """ + Fix a terrible Django manyToMany cache initialization bug by clearing the model caches. + This is only a problem with dynamically generated ManyToManys + :return: + """ + meta = model._meta + for cache_attr in ['_related_many_to_many_cache', '_m2m_cache', '_name_map']: + if hasattr(meta, cache_attr): + delattr(meta, cache_attr) + meta.init_name_map() + + +def normalize_null(value): + return value if value else None diff --git a/footprint/main/views.py b/footprint/main/views.py new file mode 100644 index 000000000..06d0d1981 --- /dev/null +++ b/footprint/main/views.py @@ -0,0 +1,62 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import os +from django import forms +from django.contrib.auth.models import User + +from django.http import HttpResponse +from django.shortcuts import render_to_response +from django.utils import simplejson +from django.core.cache import cache +from django.views.decorators.csrf import csrf_exempt +from tastypie.models import ApiKey + +class UploadFileForm(forms.Form): + title = forms.CharField(max_length=50) + file = forms.FileField() + +@csrf_exempt +def upload(request): + if request.method != 'POST': + form = UploadFileForm() + return render_to_response('footprint/upload.html', {'form': form, 'api_key':ApiKey.objects.all()[0].key}) + + api_key = request.GET.get('api_key', request.POST.get('api_key', None)) + user = User.objects.get(id=ApiKey.objects.get(key=api_key).user_id) + progress_id = request.GET.get('X-Progress-ID', 'FORM_TEST') + upload_id = '%s__%s' % (user.username, progress_id) + f = request.FILES.get('files[]', None) + # We need the extension so we know what file type to load + extension = os.path.splitext(f.name)[-1] + path = '/tmp/%s%s' % (upload_id, extension) + if not f: + return HttpResponse() + destination = open(path, 'wb+') + + for chunk in f.chunks(): + destination.write(chunk) + destination.close() + os.chmod(path, 0777) + # return status to client + return HttpResponse(simplejson.dumps(dict(upload_id=upload_id))) + +def get_upload_progress(request): + cache_key = "%s_%s" % (request.META['REMOTE_ADDR'], request.GET['X-Progress-ID']) + data = cache.get(cache_key) + return HttpResponse(simplejson.dumps(data)) + diff --git a/footprint/main/views_painting.py b/footprint/main/views_painting.py new file mode 100644 index 000000000..7fa2986d2 --- /dev/null +++ b/footprint/main/views_painting.py @@ -0,0 +1,84 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +import sys +from os import path +from django.core import serializers +from django.db import connections, transaction +from django.views.generic.simple import direct_to_template +from footprint.main.lib.functions import merge, map_to_dict +from footprint.main.utils.utils import table_name_only, string_not_empty, getSRIDForTable +from footprint.main.views import scenario_context +from django.conf import settings +sys.path.append(settings.CALTHORPE_ENGINE_PATH) +from django.utils.safestring import mark_safe +import TileStache +from TileStache.Config import buildConfiguration +from django.contrib.auth.decorators import login_required +from django.http import HttpResponse +from django.shortcuts import render_to_response +from django.template.context import RequestContext +from django.utils import simplejson +from footprint.main.models.config.scenario import Scenario + + +def get_config(): + with open(path.join(settings.STATIC_ROOT, "js/tile_stache_config.js")) as f: + json = simplejson.load(f) + return buildConfiguration(json) + +@login_required +def tilestache(request, layer_name, z, x, y, extension): + """ + Proxy to tilestache + {X} - coordinate column. + {Y} - coordinate row. + {B} - bounding box. + {Z} - zoom level. + {S} - host. + """ + config = get_config() + path_info = "%s/%s/%s/%s.%s" % (layer_name, z, x, y, extension) + coord, extension = TileStache.splitPathInfo(path_info)[1:] + mimetype, content = TileStache.getTile(config.layers[layer_name], coord, extension) + return HttpResponse(content, mimetype=mimetype) + +@login_required +def view_tilestache_map(request, scenario_id): + return tilestache_map(request, scenario_id, False) + +@login_required +def view_tilestache_map_fullscreen(request, scenario_id): + return tilestache_map(request, scenario_id, True) + +def tilestache_map(request, scenario_id, fullscreen): + scenarios = Scenario.objects.filter(id=scenario_id) + scenario = scenarios[0] + + config = get_config() + layer_urls = [] + for l in config.layers: + if isinstance(config.layers[l].provider, TileStache.Providers.Vector.Provider): + # TODO reverse doesn't work on tiles (probably because it's not in views.py) Thus we are forced to use the EXTERNAL_HOST_URL. + #layer_urls.append(reverse('tilestache', current_app='main', args=[l, '{Z}', '{X}', '{Y}', 'geojson']).replace('%7B', '{').replace('%7D', '}')) # FAILS to resolve the tilestache view + layer_urls.append(settings.EXTERNAL_HOST_URL + "/footprint/tilestache/{0}/{1}/{2}/{3}.{4}".format(l, '{Z}', '{X}', '{Y}', 'geojson').replace('%7B', '{').replace('%7D', '}')) + return render_to_response('footprint/scenario_polymaps.html', + merge({ + 's':scenario, + 'layerUrls' : mark_safe(simplejson.dumps(layer_urls)), + }), context_instance=RequestContext(request)) diff --git a/footprint/pip-req.txt b/footprint/pip-req.txt new file mode 100644 index 000000000..43d41a78f --- /dev/null +++ b/footprint/pip-req.txt @@ -0,0 +1,61 @@ +Django==1.4.10 +South==0.8.4 +psycopg2==2.4.5 +django-grappelli==2.4.2 +celery[redis]==3.1.6 +flower>=0.6,<0.7 +django-celery + +#https://github.com/muhuk/cuisine-postgresql/issues/1 cuisine-postgresql needs to be before Fabric +cuisine +cuisine-postgresql +Fabric +newrelic +ipython==0.13 +django-jsonify==0.2.1 +wadofstuff-django-serializers==1.1.0 +cElementTree +sphinx-me==0.1.2 +PIL==1.1.7 +ModestMaps==1.4.1 +TileStache==1.49.8 +python-memcached==1.48 +Shapely==1.2.16 +django-reversion==1.7.1 +django-reversion-compare==0.3.5 +django-draft==0.2.2 +django-picklefield==0.2.1 +geojson==1.0.1 +django-model-utils==2.0 +django-csv-importer==0.1.3.5 +#gevent +#-e git://github.com/abourget/gevent-socketio.git#egg=gevent-socketio +#-e git+https://github.com/liris/websocket-client.git#egg=websocket +#django-socketio +nodeenv==0.6.5 +django-tastypie==0.9.14 +gunicorn==0.17.4 +django-data-tools==0.1 +pytz +django-sendfile +uuid +boto +inflection==0.2.0 +sarge + +# testing packages +coverage==3.6 +pyflakes==0.7.2 +pep8==1.4.5 +django-jenkins==0.14.0 +behave +django-extensions==1.1.1 +django-nose==1.1 +pygraphviz==1.2 +# Memory Profiler and reqs +psutil +matplotlib +memory_profiler + +# Guppy/Heapy alternative memory profiler +guppy diff --git a/footprint/recreate_built_form_fixtures.sh b/footprint/recreate_built_form_fixtures.sh new file mode 100644 index 000000000..6df7338c9 --- /dev/null +++ b/footprint/recreate_built_form_fixtures.sh @@ -0,0 +1,16 @@ +JSON_DIR=$UF/footprint/initialization/fixtures/client/default/built_form/json_fixtures + +./manage.py dumpdata calthorpe.footprint.buildingusepercent --indent 2 > $JSON_DIR/building_uses_and_attributes.json +sed -i -e "1,3d" $JSON_DIR/building_uses_and_attributes.json + +./manage.py dumpdata calthorpe.footprint.placetypecomponentpercent --indent 2 > $JSON_DIR/placetype_components.json +sed -i -e "1,3d" $JSON_DIR/placetype_components.json + +./manage.py dumpdata calthorpe.footprint.infrastructuretype --indent 2 > $JSON_DIR/infrastructuretypes.json +sed -i -e "1,3d" $JSON_DIR/infrastructuretypes.json + +./manage.py dumpdata calthorpe.footprint.buildingpercent --indent 2 > $JSON_DIR/building_percents.json +sed -i -e "1,3d" $JSON_DIR/building_percents.json + +./manage.py dumpdata calthorpe.footprint.sacogplacetype --indent 2 > $JSON_DIR/sacog_placetype.json +sed -i -e "1,3d" $JSON_DIR/sacog_placetype.json diff --git a/footprint/settings.py b/footprint/settings.py new file mode 100644 index 000000000..863a39ee5 --- /dev/null +++ b/footprint/settings.py @@ -0,0 +1,394 @@ +from __future__ import absolute_import +from django.conf import global_settings +DEV = True +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# # Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + + +# celery generic +CELERY_TASK_RESULT_EXPIRES = 18000 # 5 hours. +CELERY_RESULT_PERSISTENT = True + +# Django settings for footprint project. +import os +import sys +import datetime + +SOCKETIO_HOST = '0.0.0.0' +SOCKETIO_PORT = '8000' + +# Set this to the client key in local_settings to configure the system for the specified client +CLIENT = 'default' +# This is used so that public tables specific to all clients are created by South +# This allows us to have a single migration path across deployments (see models/__init__.py) +ALL_CLIENTS = ['sacog', 'scag', 'demo'] + +DEBUG = False +TEMPLATE_DEBUG = DEBUG + +USE_LOCAL_SAMPLE_DATA_SETS = True +USE_SAMPLE_DATA_SETS = True + +# Set the source for built form imports, or don't import them at all +IMPORT_BUILT_FORMS = 'JSON' # set to 'CSV' to run full import, 'JSON' to use fixtures, or 'FALSE' to skip import +# Skip slow calculations for testing +SKIP_ALL_BUILT_FORMS = False +TEST_SKIP_BUILT_FORM_COMPUTATIONS = False +# Attempts to import features from the 'import' database +EXTERNALLY_IMPORT_FEATURES = True + +# Enables or disables geoserver. Disabling it speeds up tests +# The current tastypie API version +API_VERSION = 1 +API_PATH = "/footprint/api/v{0}".format(API_VERSION) + +ROOT_PATH = os.path.dirname(__file__) +PROJECT_ROOT = os.path.dirname(ROOT_PATH) + +IMPORT_BASE_FEATURE = False + +LOG_FILE = os.path.join(ROOT_PATH, 'debug.log') + +ADMINS = ( + ('Andy Likuski', 'andy@calthorpe.com'), + ('Evan Babb', 'evan@calthorpe.com'), + ('Nick Wilson', 'nick@calthorpe.com'), +) + +LOGIN_REDIRECT_URL = '/footprint' + +MANAGERS = ADMINS + +POSTGIS_VERSION = (1, 5, 3) +#POSTGIS_SQL_PATH = '/usr/share/postgresql/9.1/contrib/postgis-1.5/' + +DATABASES = { + 'default': { + 'ENGINE': 'django.contrib.gis.db.backends.postgis', + 'NAME': 'gis_django', + 'USER': 'postgres', + 'PASSWORD': 'postgres', + 'HOST': 'localhost', + 'PORT': '5432', + }, + 'import': { + 'ENGINE': 'django.contrib.gis.db.backends.postgis', + 'NAME': 'gis_django', + 'USER': 'postgres', + 'PASSWORD': 'postgres', + 'HOST': 'localhost', + 'PORT': '5432', + }, + 'sample_data': dict( + ENGINE='django.contrib.gis.db.backends.postgis', + HOST='localhost', + NAME='sample_data', + USER='calthorpe', + PASSWORD='[PASSWORD]' + ) +} +CALTHORPE_ENGINE_PATH = os.path.join(ROOT_PATH, 'engines') + +TIME_ZONE = 'America/Los_Angeles' +USE_TZ = True +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' +DEFAULT_CHARSET = 'utf-8' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True +# If you set this to False, Django will not format dates, numbers and # calendars according to the current locale +USE_L10N = True + +STATICFILES_FINDERS = ( + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.DefaultStorageFinder", +) + +# Absolute path to the directory that holds media. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = '/srv/calthorpe_media' + +# This is used for the development server, only when DEBUG = True +STATIC_DOC_ROOT = MEDIA_ROOT + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com", "http://example.com/media/" +MEDIA_URL = '/media/' +STATIC_URL = '/static/' + +TEMP_DIR = '/tmp/' + +STATIC_ROOT = '/srv/calthorpe_static' + +ADMIN_MEDIA_PREFIX = STATIC_URL + "grappelli/" + +# Make this unique, and don't share it with anybody. +SECRET_KEY = '$z7yrc#(il44#+y8y2gwfv8g8u%b+gx!pv16q9%@5l=jl9zx6p' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', + 'django.template.loaders.eggs.Loader' +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.transaction.TransactionMiddleware', + 'reversion.middleware.RevisionMiddleware', +) + +TEMPLATE_CONTEXT_PROCESSORS = ( + "django.contrib.auth.context_processors.auth", + "django.core.context_processors.debug", + "django.core.context_processors.i18n", + "django.core.context_processors.media", + "django.contrib.messages.context_processors.messages", + "django.core.context_processors.static", + +) + +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', +) + +ROOT_URLCONF = os.path.basename(ROOT_PATH) + '.urls' + +TEMPLATE_DIRS = ( + os.path.join(ROOT_PATH, 'templates'), + os.path.join(ROOT_PATH, 'main/templates') +) + +FOOTPRINT_TEMPLATE_DIR = os.path.join(ROOT_PATH, "main/templates/footprint") + +FIXTURE_DIRS = ( + os.path.join(ROOT_PATH, 'fixtures'), +) + +TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' + +DJANGO_APPS = ( + 'django.contrib.webdesign', + 'django.contrib.staticfiles', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.gis', +) + +THIRD_PARTY_APPS = ( + 'django_extensions', + 'draft', # draft must be listed before grapelli to enable admin form functionality + 'reversion', + 'reversion_compare', + 'grappelli', # needs to be before admin + 'django.contrib.admin', + 'django.contrib.admindocs', + 'south', + 'jsonify', + # 'djcelery', + 'django', + 'PIL', + 'ModestMaps', + 'TileStache', + 'memcache', + 'shapely', + 'tastypie', + 'behave', + 'picklefield', + 'django_nose', + 'geojson', + 'django_jenkins', + 'gunicorn', + 'datatools', + 'sendfile', + 'inflection', + 'guppy', + 'sarge', +) + +PROJECT_APPS = ( + 'footprint', + 'footprint.main', + 'footprint.client', + 'footprint.common', +) + +INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + PROJECT_APPS + +JENKINS_TASKS = ( + 'django_jenkins.tasks.with_coverage', + 'django_jenkins.tasks.django_tests', # select one django or + #'django_jenkins.tasks.dir_tests' # directory tests discovery + 'django_jenkins.tasks.run_pep8', + 'django_jenkins.tasks.run_pyflakes', + #'django_jenkins.tasks.run_jslint', + #'django_jenkins.tasks.run_csslint', + 'django_jenkins.tasks.run_sloccount', + #'django_jenkins.tasks.lettuce_tests', + 'django_jenkins.tasks.run_pylint', +) + +GRAPPELLI_ADMIN_TITLE = "UrbanFootprint - Administration. (Back to Analysis)" + +INTERNAL_IPS = ('127.0.0.1',) + +# This is the official geojson way of specifying srids. Just insert the SRID in the {0} +SRID_PREFIX = 'urn:ogc:def:crs:EPSG::{0}' +DEFAULT_SRID = 4326 # X/Y values, as opposed to Spherical Mercator (EPSG:900913 or 3857) which is meters +# These are the bounds used for 4326, since the project extends infinitely toward the poles +DEFAULT_SRID_BOUNDS = [-20037508.34, -20037508.34, 20037508.34, 20037508.34] + +SERIALIZATION_MODULES = { + 'json': 'wadofstuff.django.serializers.json' +} + +# Here we define the default paths that UrbanFootprint will use during installation and stuff + +CALTHORPE_DATA_DUMP_LOCATION = '/srv/datadump' + +SENDFILE_ROOT = os.path.join(MEDIA_ROOT, "downloadable") + +SENDFILE_URL = '/downloads' +# if you wanted to exclude a folder, add "--exclude 'public/cache/*'" +CALTHORPE_DAILY_DUMP_RSYNC_EXTRA_PARAMS = '' + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'verbose': { + 'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s' + }, + 'simple': { + 'format': '%(levelname)s %(message)s' + }, + }, + 'filters': { + }, + 'handlers': { + 'null': { + 'level': 'DEBUG', + 'class': 'django.utils.log.NullHandler', + }, + 'console': { + 'level': 'DEBUG', + 'class': 'logging.StreamHandler', + 'stream': sys.stdout + #'formatter': 'simple' + }, + }, + 'loggers': { + 'django': { + 'handlers': ['null'], + 'propagate': False, + 'level': 'INFO', + }, + 'django.db.backends': { + 'handlers': ['null'], # Quiet by default! + 'propagate': False, + 'level': 'DEBUG', + }, + } +} + +if DEBUG: + SENDFILE_BACKEND = 'sendfile.backends.development' +else: + SENDFILE_BACKEND = 'sendfile.backends.nginx' + +DOWNLOAD_FILE_EXPIRY = datetime.timedelta(days=1) + +from celery.schedules import crontab + +CELERYBEAT_SCHEDULE = { + 'run_cleanup_database': { + 'task': 'footprint.main.tasks.cleanup_export_job', + 'schedule': crontab(minute='0,20,40'), + 'args': (), + 'kwargs': {'fail_silently': False}, + }, +} + +# celery settings +# CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler" + +BROKER_URL = "redis://localhost:6379/0" +CELERY_ACCEPT_CONTENT = ['json', 'pickle'] +CELERY_RESULT_BACKEND = "redis://localhost:6379/0" + +CELERY_REDIS_HOST = "localhost" +CELERY_REDIS_PORT = 6379 +CELERY_REDIS_DB = 0 +CELERY_USER = 'calthorpe' +CELERY_GROUP = 'www-data' + +CELERY_TIMEZONE = TIME_ZONE +SQL_PATH = os.path.join(STATIC_ROOT, 'sql') + +SERVER_ROOT = '/srv/calthorpe/urbanfootprint' +FAB_EXEC = '/srv/calthorpe_env/bin/fab' + +PATH_CONFIGURATIONS = { + 'default': dict( + ROOT='/srv/', + GIT_ROOT='/srv/calthorpe', + + BASE_PATH='/srv/calthorpe/urbanfootprint/', + PYTHON_INTERPRETER='/srv/calthorpe_env/bin/python', + SERVER_ROOT='/srv/calthorpe/urbanfootprint/', + PROJ_ROOT='/srv/calthorpe/urbanfootprint/footprint/', + WEBSOCKETS_ROOT='/srv/calthorpe/urbanfootprint/websockets', + ), + 'jenkins': dict( + ROOT='/var/lib/jenkins/jobs/FootprintTests/workspace', + GIT_ROOT='/var/lib/jenkins/jobs/FootprintTests/workspace', + BASE_PATH='/var/lib/jenkins/jobs/FootprintTests/workspace', + PYTHON_INTERPRETER='/var/lib/jenkins/jobs/FootprintTests/workspace/calthorpe_ve/bin/python', + SERVER_ROOT='/var/lib/jenkins/jobs/FootprintTests/workspace', + PROJ_ROOT='/var/lib/jenkins/jobs/FootprintTests/workspace/footprint', + WEBSOCKETS_ROOT='/var/lib/jenkins/jobs/FootprintTests/workspace/websockets' + ) +} + +try: + from footprint.local_settings import * +except ImportError: + print "WARNING: no local settings found for this project" + +REQUIRED_SHAPEFILE_TYPES = ['.shp', '.shx', '.dbf'] +OPTIONAL_SHAPEFILE_TYPES = ['.prj'] +EXTRA_SHAPEFILE_TYPES = ['.sbn', '.sbx', '.fbn', '.fbx', '.ain', '.aih', '.ixs', '.mxs', '.atx', '.shp.xml', '.cpg'] + +SHAPEFILE_TYPES = REQUIRED_SHAPEFILE_TYPES + OPTIONAL_SHAPEFILE_TYPES + EXTRA_SHAPEFILE_TYPES + +STATICFILES_DIRS = ( + os.path.join(PROJECT_ROOT, 'footprint/main/samples'), +) diff --git a/footprint/tasks.py b/footprint/tasks.py new file mode 100644 index 000000000..083476227 --- /dev/null +++ b/footprint/tasks.py @@ -0,0 +1,56 @@ +from __future__ import absolute_import + +__author__ = 'calthorpe_associates' + +import os + +from django.utils import timezone + +from django.conf import settings +from footprint.main.models import Job +from footprint.celery import app + +from footprint.main.publishing.data_export_publishing import _export_layer +from footprint.main.publishing.config_entity_publishing import _post_save_publishing + +import sys +import traceback +from django.utils import timezone +from footprint.celery import app +from footprint.common.utils.websockets import send_message_to_client + +# +# def fp_job(function): +# +# @app.task #(name=function.func_name) +# def tracked_job(job, *args): +# job.status = 'Started' +# job.save() +# +# try: +# function(job, *args) +# job.status = 'Complete' +# # +# except Exception, e: +# job.status = "Failed" +# exc_type, exc_value, exc_traceback = sys.exc_info() +# readable_exception = traceback.format_exception(exc_type, exc_value, exc_traceback) +# job.data = readable_exception +# send_message_to_client(job.user.id, dict(event=job.type + " failed", trace=readable_exception)) +# +# job.ended_on = timezone.now() +# job.save() +# +# return tracked_job + + +@app.task +def cleanup_export_job(): + jobs_to_clean = Job.objects.filter(type="_export_layer", status="Complete") + for job in jobs_to_clean: + if timezone.now() - job.ended_on > settings.DOWNLOAD_FILE_EXPIRY: + filepath = settings.SENDFILE_ROOT + job.data + os.remove(filepath) + job.delete() + else: + continue \ No newline at end of file diff --git a/footprint/uf_tools.py b/footprint/uf_tools.py new file mode 100644 index 000000000..02e63b6a9 --- /dev/null +++ b/footprint/uf_tools.py @@ -0,0 +1,350 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com +import subprocess +from django.db import connections, transaction, connection +import psycopg2 +from footprint.main.utils.utils import database_settings + + +def connect(conn_string): + '''given a connection string, connects to the database and returns a cursor ''' + try: gDB = psycopg2.connect( conn_string ) + except Exception, E: + print str(E) + raise + return gDB + + +def get_conn_string(db_name): + d = database_settings(db_name) + return 'dbname=' + d['NAME'] + ' host=' + d['HOST'] + ' user=' + d['USER'] + ' password=' + d['PASSWORD'] + + +def db_table_exists(table, cursor=None): + try: + if not cursor: + from django.db import connection + cursor = connection.cursor() + if not cursor: + raise Exception + try: + selection = """SELECT tablename FROM pg_tables""" + cursor.execute("SELECT tablename FROM pg_tables") + + except: + raise + table_names = cursor.fetchall() + tables = [] + for t in table_names: + tables.append(t[0]) + # table_names = connection.introspection.get_table_list(cursor) + except: + raise Exception("unable to determine if the table '%s' exists" % table) + else: + return table in tables + + +@transaction.commit_on_success +def executeSQL_now(conn_string, sqls, db=None, **kwargs): + """ + :param conn_string: + :param sqls: + :param db: + :param kwargs: + :return: + """ + cursor = connection.cursor() + resultset = [] + + for sql in sqls: + try: + cursor.execute(sql) + result = cursor.fetchall() + + results = [] + for n in result: + results.append(n) + resultset.append(results) + + except Exception, E: + resultset.append(E) + + return resultset + +def dictfetchall(cursor): + """ + Wraps the cursor results into dicts. + Call this after cursor.execute() + :param cursor: + :return: + """ + desc = cursor.description + return [ + dict(zip([col[0] for col in desc], row)) + for row in cursor.fetchall() + ] + +def get_geom_reg(conn_string, schema, table): + #TODO: update this to use the views.geometry columns + query = "select srid, type, coord_dimension from views.geometry_columns " \ + "where f_table_schema = '{0}' and f_table_name = '{1}'".format(schema, table) + return executeSQL_now(conn_string, [query])[0][0] + +def list_all_geom_tables(conn_string): + query = "select table_schema, table_name from information_schema.columns where column_name = 'wkb_geometry';" + return executeSQL_now(conn_string, [query]) + +def get_constraints(conn_string, schema, table): + query = """select constraint_name from information_schema.constraint_column_usage + where table_schema = '{0}' and table_name = '{1}'""".format(schema, table) + return executeSQL_now(conn_string, [query]) + +# return qualities of the geometry field + +def get_geom_type(conn_string, schema, table): + query = "select distinct(geometrytype(wkb_geometry)) from {0}.{1}".format(schema, table) + return executeSQL_now(conn_string, [query]) + +def get_geom_SRID(conn_string, schema, table): + query = "select distinct(st_srid(wkb_geometry)) from {0}.{1}".format(schema, table) + return executeSQL_now(conn_string, [query]) + +def get_geom_dims(conn_string, schema, table): + query = "select distinct(ST_CoordDim(wkb_geometry)) from {0}.{1}".format(schema, table) + return executeSQL_now(conn_string, [query]) + +# add constraints functions + +def add_constraint_geom_type(conn_string, schema, table, geom_type): + add_constraint = "ALTER TABLE {0}.{1} ADD CONSTRAINT enforce_geotype_wkb_geometry check(geometrytype(wkb_geometry) = '{2}');".format(schema, table, geom_type) + executeSQL_now(conn_string, [add_constraint]) + + +def add_constraint_SRID(conn_string, schema, table, srid): + add_constraint = "ALTER TABLE {0}.{1} ADD CONSTRAINT enforce_srid_wkb_geometry check (st_srid(wkb_geometry) = '{2}');".format(schema, table, srid) + executeSQL_now(conn_string, [add_constraint]) + +def add_constraint_geom_dims(conn_string, schema, table, geom_dims): + add_constraint = "ALTER TABLE {0}.{1} ADD CONSTRAINT enforce_dims_wkb_geometry check (ST_CoordDim(wkb_geometry) = {2});".format(schema, table, geom_dims) + executeSQL_now(conn_string, [add_constraint]) + +# drop a constraint +def drop_spatial_constraint(conn_string, schema, table, type, column): + query = 'ALTER TABLE {0}.{1} DROP CONSTRAINT enforce_{2}_{3};'.format(schema, table, type, column) + executeSQL_now(conn_string, [query]) + +# add geometric index +def add_geom_idx(conn_string, schema, table, column="wkb_geometry"): + query = 'CREATE INDEX {0}_geom_idx on {1}.{0} using GIST ({2});'.format(table, schema, column) + executeSQL_now(conn_string, [query]) +# make sure all spatial tables in db have proper constraints +# this will ensure that they are registered in the geometry_columns view +def validate_constraints_whole_db(conn_string): + geom_tables = list_all_geom_tables(conn_string) + for t in geom_tables[0]: + schema, table = t[0], t[1] + validate_constraints(conn_string, schema, table) + +def validate_constraints(conn_string, schema, table): + info = schema + '.' + table + constraints = get_constraints(conn_string, schema, table)[0] + + # make sure the SRID has a constraint, and if not, create one + if ("enforce_srid_wkb_geometry",) not in constraints: + try: + srid = get_geom_SRID(conn_string, schema, table)[0][0] + # TODO: better error handling here -- can we try fixing the problem before rejecting it? + if len(srid) > 1: + print "there are multiple SRID's for this table, cannot add constraint" + raise + elif len(srid) == 0: + print "there is no SRID for the geometry in this table. cannot add constraint" + raise + else: + add_constraint_SRID(conn_string,schema,table,srid[0]) + info += ': added SRID constraint, ' + except Exception, E: + print E + raise + else: info += ': SRID OK, ' + + # make sure the geometry type has a constraint, and if not, create one + if ("enforce_geotype_wkb_geometry",) not in constraints: + try: + geom_type = get_geom_type(conn_string, schema, table)[0][0] + # TODO: better error handling here -- can we try fixing the problem before rejecting it? + if len(geom_type) > 1: + print "there are multiple geometry types for this table. cannot add constraint" + raise + elif len(geom_type) == 0: + print "there is no geometry type for the geometry in this table. cannot add constraint" + raise + else: + add_constraint_geom_type(conn_string, schema, table, geom_type[0]) + info += ' added geom_type constraint, ' + except Exception, E: print E; raise + + else: info += ' geom_type OK, ' + + # make sure the number of dimensions has a constraint, and if not, create one + if ("enforce_dims_wkb_geometry",) not in constraints: + try: + dims = get_geom_dims(conn_string, schema, table)[0][0] + # TODO: better error handling here -- can we try fixing the problem before rejecting it? + if len(dims) > 1: + print "there are inconsistent coordinate dimensions for this table. cannot add constraint" + raise + elif len(dims) == 0: + print "there are no coordinate dimensions for the geometry in this table. cannot add constraint" + raise + else: + add_constraint_geom_dims(conn_string, schema, table, dims[0]) + info += ' added coord_dims constraint' + except Exception, E: + print E + raise + + else: info += ' coord_dims OK ' + + print info + +def reproject_table(conn_string, schema, table, srid): +# old_srid, type, coords = get_geom_reg(conn_string, schema, table) +# drop_spatial_constraint(conn_string,schema,table,type,'wkb_geometry') +# if old_srid <> 0: +# # TODO column doesn't exist +# #drop_spatial_constraint(conn_string, schema, table, 'srid', column) +# pass + reproject = 'UPDATE {0}.{1} set wkb_geometry = ST_setSRID(ST_transform(wkb_geometry, {2}),{2});'\ + .format(schema, table, srid) + executeSQL_now(conn_string, [reproject]) + add_geom_idx(conn_string, schema, table) + +# add_constraint_SRID(conn_string, schema, table, srid) + +def ogr_to_gdb(conn_string, schema, table, output_gdb): + + reproject_table(conn_string, schema, table, '4326') + ogr_command = 'ogr2ogr -overwrite -skipfailures -f "FileGDB" "{1}" PG:"{0}" "{2}.{3}" '\ + .format(conn_string, output_gdb, schema, table) + print ogr_command + subprocess.call(ogr_command, shell=True) + + + +def getis_ord(schema, table, field, distance, nonzero_field="wkb_geometry"): + """ + this is a first pass at a stat function ... should be moved somewhere and guard rails should be installed + to prevent / deter bad analysis + :param schema: + :param table: + :param field: + :param distance: + :param nonzero_field: + :return: + """ + stat_totals = ''' select sum({2}), count(id_grid), avg({2}) from {0}.{1} where {2} <> 0; + '''.format(schema, table, field, distance, nonzero_field) + + stat_totals = executeSQL_now('default', [stat_totals])[0] + + stat_total = stat_totals[0] + N = stat_totals[1] + mean_stat = stat_totals[2] + + print "feature_count = " + str(N) + print "mean value = " + str(mean_stat) + + n_buffer = ''' + CREATE OR REPLACE FUNCTION get_nbuffer_{2} ( + in_id_grid integer, + in_geom geometry, + {2} float, + dist float, + OUT id_grid integer, + OUT wkb_geometry geometry, + OUT {2}_{3}m float, + OUT {2} float + ) + AS + $$ + select $1 as id_grid, + $2 as wkb_geometry, + $3 as {2}, + sum(r.{2}) as {2}_{3}m + + FROM {0}.{1} r WHERE st_dwithin($2, r.wkb_geometry, $4) + AND id_grid is not Null + AND {2} <> 0; + $$ + COST 10000 + language SQL STABLE strict; + '''.format(schema, table, field, distance, nonzero_field) + + cluster_tbl = ''' + drop table if exists {0}.{1}_{2}_{3}m_tmp cascade; + + create table {0}.{1}_{2}_{3}m_tmp ( + id_grid integer, + wkb_geometry geometry, + {2} float, + {2}_{3}m float + ); + + insert into {0}.{1}_{2}_{3}m_tmp select + (f).* from ( + select get_nbuffer_{2}(id_grid, wkb_geometry, {2}, {3}) as f + from {0}.{1} where {2} <> 0) s + ; + + alter table {0}.{1}_{2}_{3}m_tmp add column getis_ord_gi float; + alter table {0}.{1}_{2}_{3}m_tmp add column expected_gi float; + + update {0}.{1}_{2}_{3}m_tmp + set getis_ord_gi = {2}_{3}m / {4}, + expected_gi = {2}_{3}m / ({5} - 1); + + drop table if exists {0}.{2}_cluster_{3}m cascade; + + '''.format(schema, table, field, distance, stat_total, N, mean_stat, nonzero_field) + + executeSQL_now('default', [n_buffer, cluster_tbl]) +# variance = '''select sum( (getis_ord_gi - {6}) ^2 ) / {5} as z_score +# from {0}.{1}_{2}_{3}m_tmp'''.format(schema, table, field, distance, stat_total, N, mean_ord) + variance = '''select variance(getis_ord_gi) from {0}.{1}_{2}_{3}m_tmp;'''.format(schema, table, field, distance) + + variance = executeSQL_now('default', [variance])[0][0] + print variance + + z_score = '''create table {0}.{2}_cluster_{3}m as select + id_grid, + wkb_geometry, + {2}, + {2}_{3}m, + getis_ord_gi, + (getis_ord_gi - expected_gi)/sqrt({4}) as z_score + from {0}.{1}_{2}_{3}m_tmp; + drop table {0}.{1}_{2}_{3}m_tmp cascade; + '''.format(schema, table, field, distance, variance) + + executeSQL_now('default', [z_score]) + + print "ok" + + + diff --git a/footprint/urls.py b/footprint/urls.py new file mode 100644 index 000000000..385073dea --- /dev/null +++ b/footprint/urls.py @@ -0,0 +1,46 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2012 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the + # GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. +# If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. +# Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. +# Phone: (510) 548-6800. Web: www.calthorpe.com + +from django.conf.urls import patterns, include, url +from django.contrib import admin, admindocs +from django.conf import settings +from django.views.generic import RedirectView + +admin.autodiscover() + +urlpatterns = patterns('') + +if settings.DEBUG: + urlpatterns += patterns( + '', + (r'^media/(?P.*)$', 'django.views.static.serve', + {'document_root': settings.STATIC_DOC_ROOT}), + ) + +urlpatterns += patterns( + '', + #('^$', redirect_to, {'url': '/main/', 'permanent': False}), + url(r'^grappelli/', include('grappelli.urls')), + url(r'^admin/doc/', include('django.contrib.admindocs.urls')), + url(r'^admin/', include(admin.site.urls)), + url(r'^footprint/', include('footprint.main.urls')), + url(r'^draft/', include('draft.urls')), + url(r'^accounts/login/$', 'django.contrib.auth.views.login'), + url(r'^accounts/logout/$', 'django.contrib.auth.views.logout'), + url(r'^$', RedirectView.as_view(url='footprint/')), + ) \ No newline at end of file diff --git a/manage.py b/manage.py new file mode 100755 index 000000000..3a3c8225a --- /dev/null +++ b/manage.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +import os, sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "footprint.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) \ No newline at end of file diff --git a/sproutcore/Buildfile b/sproutcore/Buildfile new file mode 100644 index 000000000..16c5466a2 --- /dev/null +++ b/sproutcore/Buildfile @@ -0,0 +1,21 @@ +# ========================================================================== +# Project: UrbanFootprint +# Copyright: @2013 Calthorpe Associates, LLC. +# ========================================================================== + +# This is your Buildfile, which sets build settings for your project. +# For example, this tells SproutCore's build tools that EVERYTHING +# requires the SproutCore framework. +config :all + +# In addition to this Buildfile, which gives settings for your entire project, +# each of your apps has its own Buildfile with settings specific to that app. + +# When running the debug server we need to proxy cross domain requests to the server +config :all, + :required => [:sproutcore], + :build_number => 0.1, + :build_prefix => '/tmp/sc/build', + :staging_prefix => '/tmp/sc/staging', + :cache_prefix => '/tmp/sc/cache', + :debug_prefix => '/tmp/sc/debug' diff --git a/sproutcore/README b/sproutcore/README new file mode 100644 index 000000000..6576222e3 --- /dev/null +++ b/sproutcore/README @@ -0,0 +1,6 @@ +========================================================================== +Project: UrbanFootprint +Copyright: @2012 My Company, Inc. +========================================================================== + +TODO: Describe Your Project diff --git a/sproutcore/apps/fp/Buildfile b/sproutcore/apps/fp/Buildfile new file mode 100644 index 000000000..f4bd4acb6 --- /dev/null +++ b/sproutcore/apps/fp/Buildfile @@ -0,0 +1,15 @@ +# ========================================================================== +# Project: Urbanfootprint + +# Copyright: @2013 Calthorpe Associates +# ========================================================================== + +# This is your Buildfile for your app, Footprint. This tells SproutCore +# how to build your app. These settings override those in your project +# Buildfile, which contains default settings for all apps in your project. + +# It is better to add :required targets here than in the global Buildfile. +config :fp, + :required => [:'sc-table', :'sproutcore-upload', :'footprint', :'footprint_sacog', :'footprint_sandag', :'footprint_scag', :'fp'], + :title => 'UrbanFootprint', + :favicon => '/images/favicon.ico' \ No newline at end of file diff --git a/sproutcore/apps/fp/_readme.txt b/sproutcore/apps/fp/_readme.txt new file mode 100644 index 000000000..db28750b9 --- /dev/null +++ b/sproutcore/apps/fp/_readme.txt @@ -0,0 +1,91 @@ +MODELS +------ +See the models/ and fixtures/ directories. + +The Scenario model, for example: + + ScenarioCategory: (models/scenario_model.js) + name + scenarios + + Scenario: (models/scenario_model.js) + name + population + category + +These models have fixtures with dummy data: + + ScenarioCategory.FIXTURES (fixtures/config_entity_fixtures.js) + Scenario.FIXTURES (fixtures/config_entity_fixtures.js) + + + +CONTROLLERS +----------- +See the controllers/ directory. + +The controllers mediate between the model and the view. + +In main.js, we set the content for the scenarioController: + + // main.js + MainApp.scenariosController.set('content', MainApp.scenariosContent); + +The controller and its content are defined in controllers/scenarios_controller.js. + +The controller is a tree view controller: + + // controllers/scenarios_controller.js + MainApp.scenariosController = SC.TreeController.create(); + +The content represents the root node of the tree view (SourceListView), and provides its children +by performing a query for every ScenarioCategory: + + // controllers/scenarios_controller.js + MainApp.scenariosContent = SC.Object.create({ + name: "root", + treeItemChildren: function(){ + return MainApp.store.find(SC.Query.local(MainApp.ScenarioCategory, { orderBy: 'guid' })); + }.property(), + }); + +The tree view finds this content by binding its "content" property to the controller's "arrangedObjects" property: + + // views/top_half_view.js + scenariosView: SC.ScrollView.extend({ + contentView: SC.SourceListView.extend({ + contentBinding: SC.Binding.oneWay('MainApp.scenariosController.arrangedObjects'), + contentValueKey: 'name', + }) + }) + + + +VIEWS +----- +See the views/ directory. + +The UI consists of the following view hierarchy: + + - MainPane (main_pane.js) + - headerBarView + - projectView + - projectLogoView + - projectSelectView + - projectMenuView + - clientView + - titleView + - clientLogoView + - logoutButtonView + - bodyView + - topView + - scenarioSectionView (scenario_section_view.js) + - resultSectionView (result_section_view.js) + - bottomView + - sidebarView (sidebar_view.js) + - layersView + - toolsView + - placetypesView + - mapView (map_section_view.js) + - settingsView (analytic_section_view.js - currently hidden) + \ No newline at end of file diff --git a/sproutcore/apps/fp/controllers/built_forms/built_form_color_picker_controllers.js b/sproutcore/apps/fp/controllers/built_forms/built_form_color_picker_controllers.js new file mode 100644 index 000000000..6410979da --- /dev/null +++ b/sproutcore/apps/fp/controllers/built_forms/built_form_color_picker_controllers.js @@ -0,0 +1,39 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/16/13 + * Time: 2:14 PM + * To change this template use File | Settings | File Templates. + */ + + + +Footprint.mainViewController = SC.ObjectController.create({ + + content: SC.Color.from('steelblue').set('a', 1.0), + + brighterColor: function() { + var color = this.get('content'); + + return color.mult(1.25); + }.property('cssText').cacheable(), + + brighterCssText: function() { + var brighterColor = this.get('brighterColor'); + + return brighterColor.get('cssText'); + }.property('brighterColor').cacheable(), + + darkerColor: function() { + var color = this.get('content'); + + return color.mult(0.75); + }.property('cssText').cacheable(), + + darkerCssText: function() { + var darkerColor = this.get('darkerColor'); + + return darkerColor.get('cssText'); + }.property('darkerColor').cacheable() + +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/controllers/built_forms/built_form_sets_controllers.js b/sproutcore/apps/fp/controllers/built_forms/built_form_sets_controllers.js new file mode 100644 index 000000000..8a3f5261e --- /dev/null +++ b/sproutcore/apps/fp/controllers/built_forms/built_form_sets_controllers.js @@ -0,0 +1,52 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +sc_require('controllers/scenarios/scenario_controllers'); +sc_require('controllers/controllers'); +sc_require('controllers/selection_controllers'); +sc_require('controllers/sets_controllers'); + +Footprint.builtFormSetsController = Footprint.SetsController.create({ + listControllerBinding: SC.Binding.oneWay('Footprint.scenariosController'), + property:'built_form_sets' +}); + +/*** + * The active builtFormSet bound to that of the ConfigEntity's selected BuiltFormSet so that the user can change it. + * Changing it causes the ConfigEntity to be updated on the server + * @type {*} + */ +Footprint.builtFormSetActiveController = Footprint.ActiveController.create({ + listController:Footprint.builtFormSetsController, + /*** + * Observe the active layer and change the built form set if the layer corresponds to a specific built_form_set + */ + layerActiveControllerObserver: function() { + if ((Footprint.layerActiveController.get('status') & SC.Record.READY) === SC.Record.READY) { + if (!Footprint.layerActiveController.didChangeFor('showingBuiltFormPanel', 'content')) + return this; + var built_form_set_key = Footprint.layerActiveController.getPath('configuration.built_form_set_key'); + if (built_form_set_key) { + Footprint.builtFormSetsController.selectObject( + Footprint.store.find(SC.Query.local( + Footprint.BuiltFormSet, { + conditions: 'key = {key}', + key: built_form_set_key + })).firstObject()); + } + } + }.observes('Footprint.layerActiveController.status') +}); + diff --git a/sproutcore/apps/fp/controllers/built_forms/built_forms_controllers.js b/sproutcore/apps/fp/controllers/built_forms/built_forms_controllers.js new file mode 100644 index 000000000..23feb4f9a --- /dev/null +++ b/sproutcore/apps/fp/controllers/built_forms/built_forms_controllers.js @@ -0,0 +1,213 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('models/built_form_models'); +sc_require('controllers/controllers'); +sc_require('controllers/scenarios/scenario_controllers'); +sc_require('controllers/tree_content'); +sc_require('controllers/tree_controller'); +sc_require('controllers/container_item_edit_controller'); +sc_require('controllers/selection_controllers'); + +Footprint.builtFormTagsController = Footprint.ArrayController.create(Footprint.ArrayContentSupport); + +Footprint.builtFormCategoriesTreeController = Footprint.TreeController.create({ + /*** + * + * Organizes the BuiltForms by their tags. + * @type {*|void + */ + content: Footprint.TreeContent.create({ + // Respond to configEntity changes + configEntityBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.content'), + // The container object holding nodes + nodeSetBinding: SC.Binding.oneWay('Footprint.builtFormSetActiveController.content'), + // The nodes of the tree + nodesBinding: SC.Binding.oneWay('Footprint.builtFormSetActiveController.built_forms'), + // The toOne or toMany property of the node to access the keyObject(s). Here they are Tag instances + keyProperty:'tags', + // The property of the keyObject to use for a name. Here it the 'tag' property of Tag + keyNameProperty:'tag', + // Options for sorting the BuiltForms + sortProperties: ['building_attribute_set.combined_pop_emp_density','name'], + // Reverse sorting for appropriate keys + reverseSortDict: {'building_attribute_set.combined_pop_emp_density': YES}, + + /** + * The keys of the tree, currentaly all tags in the system, to which BuiltForms might associate + * TODO these tags should be limited to those used by BuiltForms + */ + keyObjectsBinding: 'Footprint.builtFormTagsController.content', + + /** + * All media used by BuiltForms, so the user can select a Medium to go assign to the BuiltForm, or clone one. + * TODO this doesn't make sense + */ + media: function() { + return Footprint.store.find( + SC.Query.local( + Footprint.Medium, { + conditions: "key BEGINS_WITH 'built_form_'", + orderBy: 'key' + })); + }.property().cacheable() + }), + selectionDidChange: function() { + if (this.getPath('selection.firstObject')) + Footprint.statechart.sendAction('builtFormDidChange', SC.Object.create({content : this.getPath('selection.firstObject')})); + }.observes('.selection') +}); + +/*** + * The active builtForm, as dictated by the user's selection + * @type {*} + */ +Footprint.builtFormActiveController = Footprint.ActiveController.create({ + listController:Footprint.builtFormCategoriesTreeController +}); + +/*** + * Supports reading of the FlatBuiltForm versions of BuiltForms. These contain extra attributes not currentlty stored in the builtForm + * @type {SelectionSupport} + */ +Footprint.flatBuiltFormsController = SC.ArrayController.create(SC.SelectionSupport, { + allowsEmptySelection:NO +}); +/*** + * The flatBuildForm corresponding to the builtFormActiveController content + */ +Footprint.flatBuiltFormActiveController = SC.ObjectController.create({ + contentBinding:SC.Binding.oneWay('Footprint.flatBuiltFormsController*selection.firstObject') +}); + +/** + * Provides a detailed view of the media of the active BuiltForm + * @type {SelectionSupport} + */ +Footprint.builtFormMediaController = SC.ArrayController.create(SC.SelectionSupport, Footprint.ArrayContentSupport, { + allowsEmptySelection:NO, + contentBinding: SC.Binding.oneWay('Footprint.builtFormActiveController.media'), + updateSelection: function() { + this.updateSelectionAfterContentChange(); + }.observes('.firstObject') +}); + + +/***===== BuiltForm CRUD controllers =====***/ + +/*** + * A flat list of all Building records + * @type {SelectionSupport} + */ +Footprint.buildingsController = SC.ArrayController.create(SC.SelectionSupport, Footprint.RecordControllerChangeSupport, { + allowsEmptySelection:YES, + orderBy: ['name ASC'], + contentDidChangeEvent: 'buildingControllerDidChange', + selectedItemDidChangeEvent: 'selectedBuildingControllerDidChange', + sourceSelection: null, + sourceSelectionBinding: SC.Binding.oneWay('Footprint.builtFormCategoriesTreeController.selection'), + selection: function() { + if (!this.get("sourceSelection") || !this.get('content')) + return; + var builtForms = this.get('sourceSelection').filter(function(builtForm) { + return this.get('content').contains(builtForm); + }, this); + var selectionSet = SC.SelectionSet.create(); + selectionSet.addObjects(builtForms); + return selectionSet; + }.property('sourceSelection', 'controller').cacheable() +}); + +/*** + * Nested store version of the buildings for editing. The selection is bound oneWay to the main controller, so that + * when the main controller selection changes, this one updates its corresponding record + */ +Footprint.buildingsEditController = Footprint.EditArrayController.create({ + allowsEmptySelection:NO, + sourceController: Footprint.buildingsController, + isEditable:YES, + recordType: Footprint.PrimaryComponent, + orderBy: ['name ASC'] +}); + +/*** + * A flat list of all BuildingType records + * @type {SelectionSupport} + */ +Footprint.buildingTypesController = SC.ArrayController.create(SC.SelectionSupport, Footprint.RecordControllerChangeSupport, { + allowsEmptySelection:YES, + orderBy: ['name ASC'], + contentDidChangeEvent: 'buildingTypesControllerDidChange', + selectedItemDidChangeEvent: 'selectedBuildingTypesControllerDidChange', + sourceSelection: null, + sourceSelectionBinding: SC.Binding.oneWay('Footprint.builtFormCategoriesTreeController.selection'), + selection: function() { + if (!this.get("sourceSelection") || !this.get('content')) + return; + var builtForms = this.get('sourceSelection').filter(function(builtForm) { + return this.get('content').contains(builtForm); + }, this); + var selectionSet = SC.SelectionSet.create(); + selectionSet.addObjects(builtForms); + return selectionSet; + }.property('sourceSelection', 'controller') +}); + +/*** + * Nested store version of the buildingTypes for editing. The selection is bound oneWay to the main controller, so that + * when the main controller selection changes, this one updates its corresponding record + */ +Footprint.buildingTypesEditController = Footprint.EditArrayController.create({ + allowsEmptySelection:NO, + sourceController: Footprint.buildingTypesController, + isEditable:YES, + recordType: Footprint.PlacetypeComponent, + orderBy: ['name ASC'] +}); + +/*** + * A flat list of all PlaceType records + * @type {SelectionSupport} + */ +Footprint.placetypesController = SC.ArrayController.create(SC.SelectionSupport, Footprint.RecordControllerChangeSupport, { + allowsEmptySelection:YES, + orderBy: ['name ASC'], + contentDidChangeEvent: 'placetypesControllerDidChange', + selectedItemDidChangeEvent: 'selectedPlacetypesControllerDidChange', + sourceSelection: null, + sourceSelectionBinding: SC.Binding.oneWay('Footprint.builtFormCategoriesTreeController.selection'), + selection: function() { + if (!this.get("sourceSelection") || !this.get('content')) + return; + var builtForms = this.get('sourceSelection').filter(function(builtForm) { + return this.get('content').contains(builtForm); + }, this); + var selectionSet = SC.SelectionSet.create(); + selectionSet.addObjects(builtForms); + return selectionSet; + }.property('sourceSelection', 'controller') +}); + +/*** + * Nested store version of the placeTypes for editing. The selection is bound oneWay to the main controller, so that + * when the main controller selection changes, this one updates its corresponding record + */ +Footprint.placetypesEditController = Footprint.EditArrayController.create({ + allowsEmptySelection:NO, + sourceController: Footprint.placetypesController, + isEditable:YES, + recordType: Footprint.Placetype, + orderBy: ['name ASC'] +}); + + diff --git a/sproutcore/apps/fp/controllers/client_land_use_definition_controller.js b/sproutcore/apps/fp/controllers/client_land_use_definition_controller.js new file mode 100644 index 000000000..6964ae5c1 --- /dev/null +++ b/sproutcore/apps/fp/controllers/client_land_use_definition_controller.js @@ -0,0 +1,32 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +/*** + * A selection controller whose contents is a list of selectable values. Zero or one items in the list are selected, + * depending on the property values of the editController.content indicated by the property at propertyKey. If all + * of editController's content property values are identical, then that single identical item will be the selection + * of this controller. If the controller's selection is updated to a new value or no value, all the editController's + * content property values will be updated accordingly. If not all values are identical then there is no selected item. + * + */ + +sc_require('controllers/synced_selection_controller'); +sc_require('controllers/features/feature_controllers'); +sc_require('controllers/controllers'); + +Footprint.clientLandUseDefinitionController = SC.ArrayController.create(Footprint.SingleSelectionSupport, { + orderBy: ['land_use_description ASC'] +}); + diff --git a/sproutcore/apps/fp/controllers/container_item_edit_controller.js b/sproutcore/apps/fp/controllers/container_item_edit_controller.js new file mode 100644 index 000000000..2ee62ab3f --- /dev/null +++ b/sproutcore/apps/fp/controllers/container_item_edit_controller.js @@ -0,0 +1,60 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +/*** + * For items that are stored in set, like BuiltForms and Policies, this extension takes care of adding newly saved items to the active set via postSave + * @type {*} + */ +Footprint.ContainerItemEditController = SC.ObjectController.extend({ + /** + * This is the EditController of the container to which new items need to be added + */ + containerEditController: null, + /** + * The property of the container that contains the items (e.g. 'built_forms' for BuiltFormSet) + */ + containerItemProperty:null, + + /* + * Used to make sure the conterEditController.objectController is READY_CLEAN before editing the active container + */ + _containerObjectController:null, + /*** + * After saving the item we need to add it to the active container if it's new. + * TODO in the future it should be possible to add it to multiple sets based on the UI. + * @param records: The saved records + * @param created: YES if the item is newly created, NO if it was simply updated + */ + onSaved: function(records, created) { + if (created) { + var containerEditController = this.get('containerEditController'); + this.set('_containerObjectController', containerEditController.get('objectController')); + } + }, + _updatingContainer: function() { + if (this.getPath('_containerObjectContent.status')===SC.Record.READY_CLEAN) { + this.set('_containerObjectController', null); + var containerEditController = this.get('containerEditController'); + containerEditController.updateCurrent(); + var container = containerEditController.get('content'); + container.get('containerItemProperty').pushObject(this.get('content')); + containerEditController.save(); + } + }.observes('._containerObjectContent'), + + _toStringAttributes: function() { + return 'recordType state content objectController'.w(); + } +}); diff --git a/sproutcore/apps/fp/controllers/controller_mixins.js b/sproutcore/apps/fp/controllers/controller_mixins.js new file mode 100644 index 000000000..d442dcda5 --- /dev/null +++ b/sproutcore/apps/fp/controllers/controller_mixins.js @@ -0,0 +1,48 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + + Footprint.ControllerMixins = { + /** + * Denotes if the data source is ready + */ + isReady: function() { + var status = this.get('status'); + return status & SC.Record.READY; + }.property('status').cacheable(), + + /** + * Provides a summary of the status of the controller. + */ + summary: function() { + var ret = ''; + + var status = this.get('status'); + if (status & SC.Record.READY) { + var len = this.get('length'); + if (len && len > 0) { + ret = len === 1 ? "1 item" : "%@ items".fmt(len); + } else { + ret = "No items"; + } + } + if (status & SC.Record.BUSY) { + ret = "Loading..." + } + if (status & SC.Record.ERROR) { + ret = "Error" + } + return ret; + }.property('length', 'status').cacheable() +}; \ No newline at end of file diff --git a/sproutcore/apps/fp/controllers/controllers.js b/sproutcore/apps/fp/controllers/controllers.js new file mode 100644 index 000000000..e45303994 --- /dev/null +++ b/sproutcore/apps/fp/controllers/controllers.js @@ -0,0 +1,250 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +/*** + * The standardized packaging of controllers for use by views for editing and cloning + */ +Footprint.ControllerConfiguration = SC.Object.extend({ + + /*** + * An SC.ObjectController that edits one or more objects + */ + editController:null, + /*** + * A list controller, such as an ListController or TreeController that manages the objects + */ + itemsController:null, + /*** + * A controller with additional computed properties about the objects, otherwise the same as itemsController + */ + recordSetController:null, + + toString: function() { + return "%@:\n%@".fmt(sc_super(), this.toStringAttributes('editController itemsController recordSetController'.w())); + } +}); + +Footprint.SingleSelectionSupport = { + + // Whenever something changes the mixer's selection, notify the singleSelection property + // so that it updates immediate for those that are bound to/observing it + selectionObserver: function() { + this.notifyPropertyChange('singleSelection'); + }.observes('.selection'), + + /*** + * Property to read and write a single value from/to the selection property + * @param keyProp + * @param value + */ + singleSelection: function(keyProp, value) { + //Clear the selection and add the selected set of the controller + if (value !== undefined) { + this.selectObject(value); + } + else { + var selection = this.get('selection'); + return selection._objects ? selection._objects[0] : null; + } + }.property('selection').cacheable() +}; + + +Footprint.ArrayContentSupport = { + contentHasChangedObserver: function() { + if (this.getPath('content.status') & SC.Record.READY) { + this.notifyPropertyChange('firstObject'); + this._scac_contentDidChange(); + this.updateSelectionAfterContentChange(); + if (this.firstObject() !== this.get('firstObject')) + throw "firstObject did not invalidate. Should be %@ but got %@".fmt(this.firstObject(), this.get('firstObject')) + } + }.observes('*content.status') +}; + +Footprint.ArrayController = SC.ArrayController.extend(Footprint.ArrayContentSupport, { + allowsEmptySelection:NO, + allowsMultipleSelection:NO, + canDeleteContent:YES, + // Sometimes we'll destroy the item on the server if its removed from the Array. + // For some lists we'll just want to remove the item from the list, especially for Libraries + destroyOnRemoval:NO, + + toString: function() { + return this.toStringAttributes('content'.w(), {content: function(content) { + return content && content.map(function(item) { + return item.toString() + }, this).join("\n---->"); + }}); + } + +}); + +/*** + * Helps an editController stay up to date with the non-edit controller selection + * @type {{selectionDidChange: Function}} + */ +Footprint.EditControllerSupport = { + /*** + * The non-nested controller whose selection we are tracking + */ + sourceController: null, + sourceSelectionDidChange: function() { + if (this.get('content')) { + // Get the unnested store selection for the sourceController + var selectedItem = this.getPath('sourceController.selection.firstObject'); + if (!selectedItem) { + this.deselectObjects(this.getPath('selection')); + return; + } + + // Find the nested store equivalent + var nestedStoreItem = this.get('content').filter(function(item) { return item.get('id')===selectedItem.get('id')})[0]; + // If it doesn't match our selection update our selection + if (!SC.Set.create([nestedStoreItem]).isEqual(SC.Set.create(this.get('selection')))) + this.selectObject(nestedStoreItem); + } + }.observes('*sourceController.selection', '.content'), + + // If our selection goes to none selected resync with the source controller's selection + // This happens when a managing state is exited + selectionDidChange: function() { + if (this.getPath('selection.length') == 0 && + this.getPath('sourceController.selection.length') > 0) { + // Resync + this.sourceSelectionDidChange(); + } + }.observes('.selection') +}; + +/*** + * Copies the behavior of SC.ManyArray to calculate a combined status of the items. The max status of all items is returned by calculatedStatus + * @type {{contentDidChange: Function, calculateStatus: Function, _calcluateStatus: Function, calculatedStatus: null, refresh: Function, toString: Function}} + */ +Footprint.CalculatedStatusSupport = { + + // Set up observers. + contentDidChange: function() { + var observedRecords = this._observedRecords; + if (!observedRecords) observedRecords = this._observedRecords = []; + var record, i, len; + // If any items in observedRecords are not in content, stop observing them. + len = observedRecords.length; + for (i = len - 1; i >= 0; i--) { + record = observedRecords.objectAt(i); + if (!this.contains(record)) { + record.removeObserver('status', this, this.calculateStatus); + observedRecords.removeObject(record); + } + } + // If any item in content is not in observedRecords, observe them. + len = this.get('length'); + for (i = 0; i < len; i++) { + record = this.objectAt(i); + if (!observedRecords.contains(record)) { + record.addObserver('status', this, this.calculateStatus); + this.invokeOnce(this.calculateStatus); + observedRecords.pushObject(record); + } + } + }.observes('[]'), + + calculateStatus: function() { + this.invokeOnce(this._calcluateStatus); + }, + _calcluateStatus: function() { + var length = this.get('length'); + var maxStatus = 0; + for (i = 0; i < length; i++) { + var status = this.objectAt(i).get('status'); + maxStatus = status > maxStatus ? status : maxStatus; + } + var status = maxStatus || SC.Record.EMPTY; + this.setIfChanged('calculatedStatus', status); + }, + + calculatedStatus: null, + + refresh: function() { + var length = this.get('length'); + for (i = 0; i < length; i++) { + var record = this.objectAt(i); + record.refresh(); + } + }, + + toString: function() { + return sc_super() + "\n---->" + + this.map(function(item) { + return item.toString() + }, this).join("\n---->"); + } +}; + +// This controller's content is the associated (non-deleted) children of the specified recordType +// in the specified nested store. For example, set parentRecord to the selected project and recordType +// to Footprint.Scenario to get the nested-store copies of the current project's scenarios. +Footprint.EditArrayController = Footprint.ArrayController.extend(Footprint.EditControllerSupport, Footprint.CalculatedStatusSupport, { + recordType: null, + nestedStore: null, + // a property on the recordType being queried. If specified, + // it will be used as a query filter matching the value of parentRecord + // content will be null whenever parentEntityKey is specified but parentRecord is null. + parentEntityKey: null, + parentRecord: null, + + content: function() { + var nestedStore = this.get('nestedStore'), + parentRecord = this.get('parentRecord'), + recordType = this.get('recordType'), + parentEntityKey = this.get('parentEntityKey'); + if (!nestedStore || !recordType || (parentEntityKey && !parentRecord)) + return null; + return this.get('nestedStore').find(SC.Query.local(recordType, this.get('parentEntityKey') ? { + conditions: '%@ = {parentConfigEntity} AND deleted != YES'.fmt(parentEntityKey), + parentConfigEntity: this.get('nestedStore').materializeRecord( + parentRecord.get('storeKey')) + } : { + conditions: 'deleted != YES' + } + )); + }.property('recordType', 'nestedStore', 'parentRecord').cacheable() + +}); + +Footprint.RecordControllerChangeSupport = { + + contentDidChangeEvent: null, + selectedItemDidChangeEvent: null, + + /*** + * Announces a change of the content when its status matches READY + */ + contentDidChange: function() { + if (this.didChangeFor('contentOrStatusChange', 'status', 'content') && + // TODO this should just be READY_CLEAN, but our status is sometimes dirty + // We have to check content.status instead of status. When content changes + // status will still have the previous status. + [SC.Record.READY_CLEAN, SC.Record.READY_DIRTY].contains(this.getPath('content.status'))) + Footprint.statechart.sendAction(this.get('contentDidChangeEvent'), this); + }.observes('content', 'status'), + + /*** + * Announces a change of the selected item. + */ + selectedItemDidChangeObserver: function() { + if (this.get('status') & SC.Record.READY && this.getPath('selection.firstObject.status') & SC.Record.READY) + Footprint.statechart.sendAction(this.get('selectedItemDidChangeEvent'), SC.Object.create({content : this.getPath('selection.firstObject')})); + }.observes('.status', '.selection') +} diff --git a/sproutcore/apps/fp/controllers/db_entity_controllers.js b/sproutcore/apps/fp/controllers/db_entity_controllers.js new file mode 100644 index 000000000..bbd715201 --- /dev/null +++ b/sproutcore/apps/fp/controllers/db_entity_controllers.js @@ -0,0 +1,25 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +/** +* An ArrayController that loads all of the DbEntityInterests of the active ConfigEntity. +* @type {*} +*/ +Footprint.dbEntityInterestsController = SC.ArrayController.create(Footprint.ArrayContentSupport,{ + builtFormSet: null, + + configEntity:null, + configEntityBinding: SC.Binding.oneWay('Footprint.scenarioActiveController*content').single() +}); diff --git a/sproutcore/apps/fp/controllers/example_image_controller.js b/sproutcore/apps/fp/controllers/example_image_controller.js new file mode 100644 index 000000000..4757a4be6 --- /dev/null +++ b/sproutcore/apps/fp/controllers/example_image_controller.js @@ -0,0 +1,21 @@ +Footprint.controller = SC.ObjectController.create(); +Footprint.photosController = SC.ArrayController.create({ + allowsMultipleSelection: NO, + content: [ + + SC.Object.create({ + url: "built_form_images/pt_city_mixed_use/photo1.jpg", + title: "Downtown Oregon" + }), + + SC.Object.create({ + url: "built_form_images/pt_city_mixed_use/photo2.jpg", + title: "Redwood City" + }), + + SC.Object.create({ + url: "built_form_images/pt_city_mixed_use/photo3.jpg", + title: "Another city" + }) + ] +}); diff --git a/sproutcore/apps/fp/controllers/features/feature_controllers.js b/sproutcore/apps/fp/controllers/features/feature_controllers.js new file mode 100644 index 000000000..b7afe50af --- /dev/null +++ b/sproutcore/apps/fp/controllers/features/feature_controllers.js @@ -0,0 +1,137 @@ +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +// FeatureControllers are always in the scope of the active ConfigEntity. They don't load all feature instances of a class but simply pull down those that make up a selection. Therefore the Datasource always expects an id list in the query to limit them + +sc_require('controllers/layer_controllers'); +sc_require('controllers/property_controllers'); + +/*** + * Controls the active features of the configEntity that are selected based on the LayerSelection + * @type {*|void} + */ +Footprint.featuresActiveController = Footprint.ArrayController.create({ + + /*** + * Delegate SC.Observable to extend dbEntityKeyToFeatureRecordType lookup + */ + configEntityDelegateBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.configEntityDelegate'), + + // Temporary hack to alert listeners when the content is updated by an edit session + updateDate: null, + + layer:null, + layerBinding:SC.Binding.oneWay('Footprint.layerSelectionActiveController.selection_layer'), + layerStatus:null, + layerStatusBinding:SC.Binding.oneWay('*layer.status'), + + recordType: function() { + return this.get('layer') && (this.get('layerStatus') & SC.Record.READY) && this.getPath('layer.featureRecordType'); + }.property('layer', 'layerStatus').cacheable(), + + /*** + * Lookup the of Footprint.Feature subclass by db_entity_key + */ + dbEntityKeyToFeatureRecordType: function () { + return this.getPath('configEntityDelegate.dbEntityKeyToFeatureRecordType') || {}; + }.property('configEntityDelegate').cacheable(), + + calculateBounds: function() { + this.mapProperty('wkb_geometry', function(geometry) { + // TODO + }); + }.property('features').cacheable() +}); +Footprint.featuresEditController = Footprint.ArrayController.create(Footprint.EditControllerSupport, { + allowsEmptySelection:YES, + sourceController: Footprint.featuresActiveController, + isEditable:YES +}); + +/*** + * Stores the properties of the active feature class + */ +Footprint.featuresActivePropertiesController = Footprint.PropertiesController.create({ + allowsMultipleSelection:NO, + recordTypeBinding: SC.Binding.oneWay('Footprint.featuresActiveController.recordType') +}); + +Footprint.joinLayersController = SC.ArrayController.create(SC.SelectionSupport, { + allowsMultipleSelection:NO, + layerLibrary:null, + layerLibraryBinding: SC.Binding.oneWay('Footprint.layerLibraryActiveController.content'), + contentBinding: SC.Binding.oneWay('Footprint.mapLayerGroupsController.foregroundLayers'), + sortProperties: ['name'], + + /* + * When the selection is updated, update the active layerSelection + */ + selectionObserver: function() { + if (this.getPath('selection.length') && this.didChangeFor('selectionObserver', 'selection')) { + // This soils the record. Don't do so until an actual selection is made + var db_entity_keys = this.getPath('selection').mapProperty('db_entity_key').filter(function(db_entity_key) { + return db_entity_key != 'None'; + }); + Footprint.layerSelectionEditController.set('joins', db_entity_keys); + } + }.observes('.selection'), + + /* + * When the layer_selection updates, change the selection + */ + layerSelectionObserver: function() { + if (this.get('content') && Footprint.layerSelectionEditController.get('status') & SC.Record.READY) { + var joins = Footprint.layerSelectionEditController.getPath('joins'); + var layers = this.get('content').filter(function(layer) { + return (joins || []).contains(layer.get('db_entity_key')); + }); + if (!SC.Set.create(layers).isEqual(SC.Set.create(this.get('selection')))) + this.selectObjects(layers); + this.updateSelectionAfterContentChange(); + } + }.observes('.content', + 'Footprint.layerSelectionEditController.status', + 'Footprint.layerSelectionEditController.content', + 'Footprint.layerSelectionEditController.joins' + ).cacheable() +}); + +Footprint.availableFieldsController = Footprint.PropertiesController.create({ + allowsMultipleSelection:NO, + layer:null, + layerBinding: SC.Binding.oneWay('Footprint.layerActiveController.content'), + joinLayer:null, + joinLayerBinding: SC.Binding.oneWay('Footprint.joinLayersController*selection.firstObject'), + fields: function() { + var dbEntityKey = this.getPath('layer.db_entity_key'); + var joinDbEntityKey = this.getPath('joinLayer.db_entity_key'); + return (joinDbEntityKey ? + this.getPath('joinLayer.db_entity_interest.feature_fields').map(function(field) { + return '%@.%@'.fmt(joinDbEntityKey, field); + }) : [] + ).concat( + dbEntityKey ? + this.getPath('layer.db_entity_interest.feature_fields').map(function(field) { + return '%@.%@'.fmt(dbEntityKey, field); + }) : [] + ); + }.property('layer', 'joinLayer').cacheable() +}); + +/*** + * Stores features summary info. There is one instance per group-by combination + */ +Footprint.featureSummariesActiveController = Footprint.ArrayController.create({ + contentBinding: SC.Binding.oneWay('Footprint.layerSelectionActiveController.summary_results') +}); diff --git a/sproutcore/apps/fp/controllers/features/scenario_built_form_feature_controllers.js b/sproutcore/apps/fp/controllers/features/scenario_built_form_feature_controllers.js new file mode 100644 index 000000000..5bdb099e1 --- /dev/null +++ b/sproutcore/apps/fp/controllers/features/scenario_built_form_feature_controllers.js @@ -0,0 +1,27 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +sc_require('controllers/features/feature_controllers'); + +Footprint.scenarioBuiltFormFeatureEditController = SC.ObjectController.create({ + recordType: Footprint.FutureScenarioFeature, + objectControllerBinding:SC.Binding.oneWay('Footprint.scenarioBuiltFormFeatureActiveController') +}); + +Footprint.scenarioBuiltFormFeatureControllers = Footprint.ControllerConfiguration.create({ + editController:Footprint.scenarioBuiltFormFeatureEditController, + itemsController:Footprint.scenarioBuiltFormFeatureEditController, + recordSetController:Footprint.scenarioBuiltFormFeatureEditController +}); diff --git a/sproutcore/apps/fp/controllers/global_config_controllers.js b/sproutcore/apps/fp/controllers/global_config_controllers.js new file mode 100644 index 000000000..16334d18c --- /dev/null +++ b/sproutcore/apps/fp/controllers/global_config_controllers.js @@ -0,0 +1,15 @@ + +/*** + * globalConfigController binds holds the singleton global config as an array in its content to conform with the other + * ConfigEntity controllers + * @type {*} + */ +Footprint.globalConfigController = SC.ArrayController.create(); + +/*** + * References the globalConfig singleton in its content + * @type {*|void} + */ +Footprint.globalConfigActiveController = Footprint.ActiveController.create({ + listController:Footprint.globalConfigController +}); diff --git a/sproutcore/apps/fp/controllers/layer_controllers.js b/sproutcore/apps/fp/controllers/layer_controllers.js new file mode 100644 index 000000000..5c91ad79f --- /dev/null +++ b/sproutcore/apps/fp/controllers/layer_controllers.js @@ -0,0 +1,167 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('controllers/controllers'); +sc_require('controllers/selection_controllers'); +sc_require('controllers/sets_controllers'); +sc_require('models/presentation_models'); +sc_require('models/layer_models'); +sc_require('controllers/controller_mixins'); +sc_require('controllers/scenarios/scenario_controllers'); +sc_require('controllers/presentation_controllers'); + +Footprint.layerTagsController = Footprint.ArrayController.create(Footprint.ArrayContentSupport); + +Footprint.layerLibrariesController = Footprint.PresentationsController.create({ + contentBinding:SC.Binding.oneWay('Footprint.scenarioActiveController*presentations.layers') +}); + +Footprint.layerLibraryActiveController = Footprint.PresentationController.create({ + presentationsBinding:SC.Binding.oneWay('Footprint.layerLibrariesController.content'), + key: 'layer_library__default', + keysBinding:SC.Binding.oneWay('.layers').transform(function(value) { + if (value && value.get('status') & SC.Record.READY) + return value.mapProperty('db_entity_interest').mapProperty('db_entity').mapProperty('key'); + }) +}); + +/**** + * The flat version of the active layers. This controller sends the events layersDidChange + * and layerDidChange when the whole set or active layer is updated + * @type {RecordControllerChangeSupport} + */ +Footprint.layersController = Footprint.ArrayController.create(Footprint.RecordControllerChangeSupport, { + contentBinding: SC.Binding.oneWay('Footprint.layerLibraryActiveController.layers'), + selectionBinding: SC.Binding.oneWay('Footprint.layerCategoriesTreeController.selection'), + + selectedItemDidChangeEvent:'layerDidChange', + contentDidChangeEvent:'layersDidChange' +}); + +/*** + * Nested store version of the Layers for editing. The selection is bound oneWay to the main controller, so that + * when the main controller selection changes, this one updates its corresponding record + */ +Footprint.layersEditController = Footprint.EditArrayController.create({ + allowsEmptySelection:YES, + sourceController: Footprint.layersController, + recordType: Footprint.Layer, + parentEntityKey: 'presentation', + parentRecordBinding: 'Footprint.layerLibraryActiveController.content', + nestedStore: null +}); + +Footprint.layerEditController = SC.ObjectController.create({ + contentBinding: SC.Binding.oneWay('Footprint.layersEditController*selection.firstObject') +}); + +Footprint.layerCategoriesTreeController = Footprint.TreeController.create({ + + content: Footprint.TreeContent.create({ + // Respond to configEntity changes + configEntityBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.content'), + // The container object holding nodes + nodeSetBinding: SC.Binding.oneWay('Footprint.layerLibraryActiveController.content'), + // The nodes of the tree + nodesBinding: SC.Binding.oneWay('Footprint.layerLibraryActiveController.layers'), + nodeStatus: null, + nodeStatusBinding: SC.Binding.oneWay('*nodes.status'), + + // The toOne or toMany property of the node to access the keyObject(s). Here they are Tag instances + keyProperty:'tags', + // The property of the keyObject to use for a name. Here it the 'tag' property of Tag + keyNameProperty:'tag', + // Our default tag for layers whose db_entity doesn't have any tags that match are tag se + undefinedKeyObject:SC.Object.create({tag:'Untagged'}), + // Options for sorting the BuiltForms + sortProperties: ['name'], + + /** + * The keys of the tree, currentaly all tags in the system, to which BuiltForms might associate + * TODO these tags should be limited to those used by BuiltForms + */ + keyObjectsBinding: 'Footprint.layerTagsController.content' + }), + + allowsEmptySelection: NO, + + nodesStatus: null, + nodesStatusBinding: SC.Binding.oneWay('*nodes.status'), + contentDidChange: function() { + if (this.get('nodes') && (this.getPath('nodesStatus') & SC.Record.READY) && + this.didChangeFor('layerCategoriesTreeControllerContent', 'nodes', 'nodesStatus')) { + // Tell the selection that the content has changed + this.updateSelectionAfterContentChange(); + // Select the first visible layer that isn't a background layer + var selectLayer = this.get('nodes').filter(function(layer) { + return layer.get('applictionVisible') && layer.get('status') & SC.Record.READY && + !layer.get('tags').mapProperty('tag').contains('background_imagery'); + })[0] || this.getPath('nodes.firstObject'); + this.selectObject(selectLayer); + } + }.observes('.nodes', '.nodesStatus'), + + /*** + * Initialize the selection when there is no selection yet but everything is ready + */ + initialialSelectionObserver: function() { + if (!this.getPath('selection.length') && Footprint.layerLibraryActiveController.getPath('layers.status') & SC.Record.READY && this.get('nodes')) { + // Find the first visible layer + this.selectObject(this.get('nodes').filter(function(layer) { + return layer.get('applicationVisible'); + })[0]); + } + }.observes('Footprint.layerLibraryActiveController*layers.status', '.nodes').cacheable() +}); + +/*** + * Binds to the currently selected PresentationMedium + * @type {*} + */ +Footprint.layerActiveController = SC.ObjectController.create({ + contentBinding: SC.Binding.oneWay('Footprint.layerCategoriesTreeController*selection.firstObject') +}); + +Footprint.layerSelectionsController = SC.ArrayController.create(SC.SelectionSupport, Footprint.ArrayContentSupport, { + // Update the layer property whenever the user layer or user statuses change + activeLayer:null, + activeLayerBinding:SC.Binding.oneWay('Footprint.layerActiveController.content'), + user:null, + // TODO firstObject simple binding doesn't work + userBinding:SC.Binding.oneWay('Footprint.userController.firstObject'), + contentObserver:function() { + if (this.get('content') && this.get('status') & SC.Record.READY) { + this.forEach(function(layerSelection) { + if (layerSelection.get('user') === this.get('user')) { + this.selectObject(layerSelection); + } + }, this) + } + }.observes('.content', '.status').cacheable(), +}); + +/*** + * Binds to the LayerSelection instance of the active Layer + * @type {*|void} + */ +Footprint.layerSelectionActiveController = SC.ObjectController.create({ + contentBinding: SC.Binding.oneWay('Footprint.layerSelectionsController.firstObject'), + layerId: null, + layerIdBinding: SC.Binding.oneWay('*content.layer.id') +}); + +Footprint.layerSelectionEditController = SC.ObjectController.create({ + layerId: null, + layerIdBinding: SC.Binding.oneWay('*content.layer.id'), +}); diff --git a/sproutcore/apps/fp/controllers/loading_controllers.js b/sproutcore/apps/fp/controllers/loading_controllers.js new file mode 100644 index 000000000..e2c789430 --- /dev/null +++ b/sproutcore/apps/fp/controllers/loading_controllers.js @@ -0,0 +1,22 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.loadingStatusController = SC.ObjectController.create({ + content:0, + maximum:10, + increment:function(value) { + this.set('content', this.get('content')+(value || 1)); + } +}); diff --git a/sproutcore/apps/fp/controllers/login_controller.js b/sproutcore/apps/fp/controllers/login_controller.js new file mode 100644 index 000000000..dd1f73cc6 --- /dev/null +++ b/sproutcore/apps/fp/controllers/login_controller.js @@ -0,0 +1,18 @@ +// ========================================================================== +// Project: Footprint.loginController +// Copyright: @2012 My Company, Inc. +// ========================================================================== +/*globals Footprint */ + +/** @class + + Holds authentication data during login. + + @extends SC.Object +*/ + +Footprint.loginController = SC.Object.create({ + username: null, + password: null, + api_key: null +}); diff --git a/sproutcore/apps/fp/controllers/map_controllers.js b/sproutcore/apps/fp/controllers/map_controllers.js new file mode 100644 index 000000000..045e1905c --- /dev/null +++ b/sproutcore/apps/fp/controllers/map_controllers.js @@ -0,0 +1,299 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + + /*** + * A lightweight controller for basic map functionality. + * @type {Class} + */ +Footprint.mapController = SC.ObjectController.create({ + + content:null, + selectionLayerNeedsRefresh: NO, + refreshSelectionLayer: function() { + this.set('selectionLayerNeedsRefresh', YES); + }, + layerNeedsRefresh:NO, + refreshLayer: function() { + this.set('layerNeedsRefresh', YES); + }, + + mapNeedsRezoomToProject:NO, + resetExtentToProject: function(){ + this.set('mapNeedsRezoomToProject', YES); + }, + + mapNeedsRezoomToSelection:NO, + resetExtentToSelection: function(){ + this.set('mapNeedsRezoomToSelection', YES); + }, + + mapLayersNeedZoomUpdate:NO, + refreshMapLayerZoomVisibility: function() { + this.set('mapLayersNeedZoomUpdate', YES); + }, + + readToCreateMap:NO, + isReady:NO +}); + + /*** + * A lightweight controller that manages the map tools properties + * content is A Footprint.MapTools instance (singleton?) for painting, selection, etc. tools for the map + * This is setup by the view so that the tools can access the map and send actions to the view + * @type {Class} + */ +Footprint.mapToolsController = SC.ObjectController.create({ + + activeMapToolKey:null, + /** + * The active paint tool according to activeMapToolKey + */ + activePaintTool:null, + activeMapToolKeyObserver: function() { + var paintToolName = this.get('activeMapToolKey'); + var paintTool = paintToolName ? this.get(paintToolName) : null; + if (this.get('activePaintTool') !== paintTool) + this.set('activePaintTool', paintTool); + }.observes('.activeMapToolKey') +}); + +Footprint.mapLayerGroupsController = SC.ObjectController.create({ + + map: null, + mapBinding: SC.Binding.oneWay('Footprint.mapController.content'), + + sortedLayers: null, + sortedLayersBinding: SC.Binding.oneWay('Footprint.layerCategoriesTreeController.sortedNodes'), + + /*** + * The Footprint.User instance of the logged in user. Map selection layers are user specific, thus the + * user id is used to form those layer names. In the future when the entire Presentation instance is + * specific to a user, the user will be extracted from the map Presentation instance. + */ + user: null, + userBinding: SC.Binding.single('Footprint.userController.content'), + + /*** + * Non-background layers + */ + foregroundLayers: function() { + if (this.get('sortedLayers')) + return this.get('sortedLayers').filter(function(layer) { + return !layer.get('tags').mapProperty('tag').contains('background_imagery'); + }, this); + return []; + }.property('sortedLayers').cacheable(), + + /*** + * Background layers + */ + backgroundLayers: function() { + if (this.get('sortedLayers')) + return this.get('sortedLayers').filter(function(layer) { + return layer.get('tags').mapProperty('tag').contains('background_imagery'); + }, this) || []; + return []; + }.property('sortedLayers').cacheable(), + + /*** + * Each layer in layers has a unique db_entity_key that is used to identify it in mapLayerGroups + * This returns all of them + */ + foregroundLayerKeys: function() { + if (this.get('foregroundLayers')) + return this.get('foregroundLayers').mapProperty('db_entity_key'); + }.property('foregroundLayers').cacheable(), + + /*** + * Each layer in layers has a unique db_entity_key that is used to identify it in mapLayerGroups + * This returns all of them + */ + backgroundLayerKeys: function() { + if (this.get('backgroundLayers')) + return this.get('backgroundLayers').mapProperty('db_entity_key'); + }.property('backgroundLayers').cacheable(), + + /** + * All layer keys + */ + layerKeys: function() { + if (this.get('sortedLayers')) + return this.get('sortedLayers').mapProperty('db_entity_key'); + }.property('sortedLayers').cacheable(), + + /** + * The Footprint.Layer instance of the content which is active + */ + activeLayer: null, + // This isn't working reliably, don't know why. Setting it in layer_selection_is_ready_state + //activeLayerBinding: SC.Binding.oneWay('Footprint.layerActiveController.content'), + + /*** + * The activeLayerGroup based on the activeLayer + * @returns {*} + */ + activeLayerGroup: function() { + if (this.getPath('activeLayer.db_entity_key')) + return this.getPath(this.getPath('activeLayer.db_entity_key')); + }.property('activeLayer').cacheable(), + + _layerKeys: null, + _mapLayerGroups: null, + /*** + * Iterates through the layers of the layers and configures each layer. + * This is called whenever the layersController's content changes or one of its layers is added, edited, + * or removed + * @private + */ + layersWillChange: function() { + var map = this.get('map'); + // TODO temporary while I figure out how to wait for divs to size before creating the map + map.resize(); + + /*** + * Remove existing layers. + */ + map.remove(po.compass(map)); + if (this.get('_mapLayerGroups')) { + var mapLayerGroups = this.get('_mapLayerGroups'); + (this.get('_layerKeys') || []).forEach(function(layerKey) { + var mapLayerGroup = mapLayerGroups.get(layerKey); + if (mapLayerGroup) { + mapLayerGroup.get('mapLayerGroupLayers').forEach(function(mapLayerName) { + if (mapLayerGroup.get(mapLayerName)) + map.remove(mapLayerGroup.get(mapLayerName)); + }, this); + } + }, this) + } + this.set('_layerKeys', this.get('layerKeys')); + this.set('content', null); + }, + + /*** + * An SC.Object of Footprint.MapLayerGroup instances, keyed by db_entity_key. Each layerGroup holds a vector, raster, + * and layer_selection layer corresponding to a Footprint.Layer instance. Footprint.Layer instances are those + * of the ConfigEntity's configured map Presentation instance. + */ + updateMapLayerGroups: function() { + var map = this.get('map'); + // get active layers, create Polymaps objects for them + // layerGroups are keyed by the DbEntity name + this.set('content', mapToSCObject(this.get('sortedLayers').toArray(), function(layer) { + if (this.get('foregroundLayers').contains(layer)) { + if (!(layer.get('visible_attributes') && layer.getPath('visible_attributes.length') > 0) ) + return null; + var styleAttribute = layer.get('visible_attributes').firstObject(); + var baseLayerName = "layer_%@_%@".fmt(layer.get('id'), styleAttribute); + var vectorLayerName = "%@_%@".fmt(baseLayerName, 'vector'); + var rasterLayerName = "%@_%@".fmt(baseLayerName, 'raster'); + + // The selection layer is specific to the active user. + var userId = this.getPath('user.id'); + var selectionLayerName = "layer_%@_%@_%@_selection".fmt(layer.get('id'), styleAttribute, userId); + + var coordString = "/{Z}/{X}/{Y}"; + var tileUrl = "http://%@/footprint/tiles/".fmt(window.location.hostname); + var vectorUrl = tileUrl + vectorLayerName + coordString + ".geojson"; + var rasterUrl = tileUrl + rasterLayerName + coordString + ".png"; + var selectionUrl = tileUrl + selectionLayerName + coordString + ".geojson"; + + var vectorLayer = po.geoJson() + .url(vectorUrl) + .zoom(function(z){ + return z; + }); + + var selectionLayerStyling = po.stylist().attr('stroke', 'Yellow').attr('fill','Red;').attr('class', 'selected'); + var selectionLayer = po.geoJson() + .url(selectionUrl) + .zoom(function(z){ + return z; + }) + .on("load", selectionLayerStyling) + .on('show', selectionLayerStyling); + + var rasterLayer = po.image() + .url(rasterUrl); + + return [layer.get('db_entity_key'), Footprint.MapLayerGroup.create({ + map: map, + layer: layer, + vector: vectorLayer, + raster: rasterLayer, + selectionLayer: selectionLayer + })]; + } + else { + var db_entity = layer.getPath('db_entity_interest.db_entity'); + var rasterLayer = po.image() + .id(layer.get('db_entity_key')) + .url(po.url(db_entity.get('url')) + .hosts(db_entity.get('hosts'))) + .visible(layer.get('applicationVisible')); + return [layer.get('db_entity_key'), Footprint.MapLayerGroup.create({ + map: map, + layer: layer, + raster: rasterLayer + })]; + } + }, this)); + this.set('_mapLayerGroups', this.get('content')); + }, + + mapLayerGroupsDidUpdate: function() { + (this.get('backgroundLayerKeys') || []).forEach(function(layerKey) { + var mapLayerGroup = this.get(layerKey); + mapLayerGroup.setVisibility(); + mapLayerGroup.addLayersToMap(); + }, this); + + (this.get('foregroundLayerKeys') || []).forEach(function(layerKey) { + var mapLayerGroup = this.get(layerKey); + + if (mapLayerGroup) { + // Add all map layers to the map and set their visibility according to the mapLayerGroup's visibility + mapLayerGroup.setVisibilityBasedOnZoom(); + mapLayerGroup.addLayersToMap(); + } + else { + logWarning('Layer with key %@ does not have map layers associated with it. Does it have any visible attributes?'.fmt(layerKey) ); + } + }, this); + var map = this.get('map'); + if (map) + map.add(po.compass(map)); + }, + + /*** + * Reacts to zooming and movement by showing and hiding the raster or vector layers of visible layer groups + */ + mapLayersNeedUpdateObserver: function() { + if (!(Footprint.mapController.get('mapLayersNeedZoomUpdate') && this.get('content'))) + return; + + this.get('foregroundLayerKeys').forEach(function(layerKey) { + var mapLayerGroup = this.get(layerKey); + if (mapLayerGroup) { + mapLayerGroup.setVisibilityBasedOnZoom(); + } + else { + logWarning('Layer with key %@ does not have map layers associated with it. Does it have any visible attributes?'.fmt(layerKey) ); + } + }, this); + Footprint.mapController.set('mapLayersNeedZoomUpdate', NO); + }.observes('Footprint.mapController.mapLayersNeedZoomUpdate', '.content') +}); + diff --git a/sproutcore/apps/fp/controllers/maps/geometry_controller.js b/sproutcore/apps/fp/controllers/maps/geometry_controller.js new file mode 100644 index 000000000..072a01c09 --- /dev/null +++ b/sproutcore/apps/fp/controllers/maps/geometry_controller.js @@ -0,0 +1,21 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 2/19/13 + * Time: 9:48 AM + * To change this template use File | Settings | File Templates. + */ + +sc_require('resources/polymaps'); + +$(function() { + window.geometryFactory = new jsts.geom.GeometryFactory(); + window.rTree =new jsts.index.strtree.STRtree(); + window.reader = new jsts.io.GeoJSONReader(); +}); + +Footprint.geometryController = SC.Object.create({ + + + +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/controllers/maps/painting_controller.js b/sproutcore/apps/fp/controllers/maps/painting_controller.js new file mode 100644 index 000000000..28e393d17 --- /dev/null +++ b/sproutcore/apps/fp/controllers/maps/painting_controller.js @@ -0,0 +1,335 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.paintingController = SC.Object.create({ + + // sends geometry to a view that will use it for intersecting with a DB layer + query_result_ids: [], + + selectWithBrush: function() { + if (d3.select('.polybrush').node != null) { + this.sendPolyBrush() + } + else this.sendBrush(); + }, + + sendBrush: function() { + var bGeom = Footprint.Geometry.makeBrushGeom(); + + if (!bGeom) { + return; + } + geom = Footprint.Geometry.projectPolygon(bGeom); + Footprint.Painting.sendGeometry(geom, "run_intersection_query") + }, + + sendPolyBrush: function() { + bGeom = Footprint.Geometry.makePolybrushGeom(); + + if (!bGeom) {return;} + + geom = Footprint.Geometry.projectPolygon(bGeom); + Footprint.Painting.sendGeometry(geom, "run_intersection_query"); + }, + + activeBuiltFormId: function() { + if (Footprint.builtFormActiveController.get('content', false)) { + return Footprint.builtFormActiveController.get('content').get('id'); } + else { return false; } + }, + + updateBuiltFormWithSelection: function() { + var activeGroup = Footprint.mapLayerGroups.scenario_built_form_layer; + var layer = activeGroup.raster; + var selection = activeGroup.selection; + + if (selection.length == 0) { + SC.Logger.warn("Nothing selected!"); + return false } + else { + SC.Logger.debug("Appling built forms to %@ selectinos".fmt(selection.length)); + var featureData = this.getTableInfo(); + // var style = builtForm.color; + postData = 'feature_data=%@'.fmt(encodeURIComponent(JSON.stringify(featureData))); + + var self = this; + $.ajax({ + url : "/footprint/update_with_selection", + type : "POST", + data : postData, + dataType:'json', + error : function() { + layer.reload(); + self.deselectAll(); + }, + success : function() { + // stylePaintedParcels(builtForm.color); + layer.reload(); + self.deselectAll(); + } + }); + return true; + } + }, + + getTableInfo: function() { + var activeGroup = Footprint.mapLayerGroups.scenario_built_form_layer + var scenario = Footprint.scenarioActiveController.get('content') + var layer = activeGroup.raster; + var selection = activeGroup.selection; + return { + scenario: scenario.get('id'), + layer: Footprint.layerActiveController.get('content').toJSON(), + built_form: this.activeBuiltFormId(), + user: Footprint.userController.get('content').firstObject().toJSON() + }; + }, + + updateBuiltFormWithGeography: function() { + featureData = this.getTableInfo(); + featureData.geometry = projectPolygon(bGeom); + featureData.built_form = this.activeBuiltFormId(); + layer = Footprint.mapLayerGroups.scenario_built_form_layer + postData = "feature_data=%@".fmt(encodeURIComponent(JSON.stringify(featureData))); + + result = $.ajax({ + url : "/footprint/update_with_geography", + type : "POST", + data : postData, + error : function() { + layer.vector.reload(); + layer.raster.reload(); + }, + success : function() { + layer.raster.reload(); + layer.vector.reload(); + } + }); + }, + + deselectAll: function() { + featureData = this.getTableInfo(); + layer = Footprint.mapLayerGroups.scenario_built_form_layer + postData = "feature_data=%@".fmt(encodeURIComponent(JSON.stringify(featureData))); + result = $.ajax({ + url : "/footprint/deselect_all", + type : "POST", + data : postData, + error : function() { + layer.selection_layer.reload(); + layer.selection_layer.reload(); + }, + success : function() { + layer.selection_layer.reload(); + layer.selection_layer.reload(); + } + }); + }, + + // TODO this isn't working yet + getSelectedFeatures: function(success) { + var featureData = this.getTableInfo(); + var featureDataString = encodeURIComponent(JSON.stringify(featureData)); + $.ajax({ + url : "/footprint/get_selected_features", + type : "GET", + data : featureDataString, + error : function() { return alert('update failed') }, + success: function(data) { + success(data); + } + }); + }, + + getExtentFromSelection: function() { + var layer = findActiveLayer(); + if (layer.selection.length == 0) { return false } + + else { + var post = { + selection: layer.selection, + layer: layer.id() + }; + } + postData = $.format("feature_data={0}", + encodeURIComponent(JSON.stringify(post)) + ); + $.ajax({ + url : "/footprint/get_selection_bbox", + type : "POST", + data : postData, + error : function() { return alert('update failed') }, + success: parsePostGISExtent + }); + }, + + parsePostGISExtent: function(postGISExtent) { + var bbox = postGISExtent.bbox.replace('(','').replace(')','').replace('BOX','').split(','); + bbox[0] = bbox[0].split(" "); bbox[1] = bbox[1].split(" "); + var Extent = [{'lon': parseFloat(bbox[0][0]), 'lat': parseFloat(bbox[0][1])}, + {'lon':parseFloat(bbox[1][0]), 'lat': parseFloat(bbox[1][1])} ]; + return Extent; + }, + + getInfoFromSelection: function(field) { + var layer = findActiveLayer(); + + if (layer.selection.length == 0) { return false } + else { + + var post = { + selection: layer.selection, + field: field, + layer: layer.id() + }; + + var postData = $.format("feature_data={0}", encodeURIComponent(JSON.stringify(post))); + + $.ajax({ + url : "/footprint/scenario/1/get_info_from_selection", + type : "POST", + data : postData, + error : function() { return alert('update failed') }, + success: getInfoResponse + }); + } + }, + + selectionResponse: function(resultData) { + + var activeLayerKey = Footprint.layerActiveController.get('content').toJSON().db_entity_key; + var layerGroup = Footprint.mapLayerGroups[activeLayerKey]; + + layerGroup.selection = resultData['selected_ids']; + + if (layerGroup.vector.visible() === true) { + var parcels = d3.selectAll('.parcel_geometry'); + selectFeaturesByIdArray(parcels, resultData['selected_ids']) + } + + if (layerGroup.raster.visible() === true) { + layerGroup.selection_layer.reload(); + } + + }, + + getInfoResponse: function(resultData) { + var resultString= resultData.selected_values; + window.resultDataList.append[resultData]; + }, + + stylePaintedParcels: function(style) { + return d3.selectAll('.selected').style('fill', style).classed('selected', false); + }, + + updateParcelAttributes: function(id) { + + }, + + selectFeature: function(f, event) { + // identify a vector feature as selected to represent selection visually + var selected_class = f.element.getAttribute("class") + " selected"; + f.element.setAttribute("class", selected_class); + }, + + unselect: function() { + var sel = d3.selectAll(".selected"); + if (sel.node()!=null) { + var unsel = sel.attr("class").replace(" selected", ""); + sel.attr("class", unsel); + } + Footprint.mapLayerGroups.scenario_built_form_layer.selection = []; + Footprint.Painting.deselectAll(); + Footprint.mapLayerGroups.scenario_built_form_layer.raster.reload(); + }, + + renderSelection: function(e) { + var layer = getActiveLayer(); + if (layer == undefined) {return false} + var group = getActiveLayerGroup(); + if (group.selection.length > 0) { + for (var i = 0; i < e.features.length; i++) { + var f = d3.select(e.features[i].element); + f.classed("selected", function() { + var id = parseInt(this.id.slice(2)); + if ($.inArray(id, group.selection) == -1) {return false} + else {return true} + }) + } + } + }, + + selectFeaturesByIdArray: function(selection, array) { + // takes a d3 selection and classes them as "selected" if they have IDs contained in the array + selection.classed("selected", function() { + var id = parseInt(this.id.slice(2)); + if ($.inArray(id, array) == -1) {return false} + else {return true} + }) + }, + + selectionResponse: function(resultData) { + + var activeLayerKey = Footprint.layerActiveController.get('content').toJSON().db_entity_key; + var layerGroup = Footprint.mapLayerGroups[activeLayerKey]; + + layerGroup.selection = resultData['selected_ids']; + + if (layerGroup.vector.visible() === true) { + var parcels = d3.selectAll('.parcel_geometry'); + selectFeaturesByIdArray(parcels, resultData['selected_ids']) + } + + if (layerGroup.raster.visible() === true) { + layerGroup.selection_layer.reload(); + } + }, + + switchSelection: function(f, event) { + // a way to refine the selections, whether you are clicking on svg or png tiles + var layer = getActiveLayerGroup(); + if (layer.raster.visible() === true) { + rasterSelectionRefinement(f, event, layer); + } + if (layer.vector.visible() === true) { + vectorSelectionRefinement(f, event, layer); + } + }, + + vectorSelectionRefinement: function(f, event) { + // this just styles the parcel correctly and adds the element to the selection array stored in the layer + // we need to send this selection layer to the server before we can run a update_with_selection + var layer = getActiveLayerGroup(); + var g = d3.select(f.element); + + var id = parseInt(g.node().id.slice(2)); + var selected = g.classed('selected') == true; + + if (selected) { + var index = selectionArray.indexOf(id); + layer.selection.splice(index, 1); + } + + else { + layer.selection.push(id); + } + + g.classed("selected", !selected) + }, + + rasterSelectionRefinement: function(event, layer) { + coord = po.pointLocation(map.mouse) + } +}); diff --git a/sproutcore/apps/fp/controllers/policies/analytic_controllers.js b/sproutcore/apps/fp/controllers/policies/analytic_controllers.js new file mode 100644 index 000000000..2a21f65ea --- /dev/null +++ b/sproutcore/apps/fp/controllers/policies/analytic_controllers.js @@ -0,0 +1,12 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 1/13/14 + * Time: 2:45 PM + * To change this template use File | Settings | File Templates. + */ + + +Footprint.showingAnalyticModuleController = SC.ObjectController.create({ + nowShowing: null +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/controllers/policies/policies_controllers.js b/sproutcore/apps/fp/controllers/policies/policies_controllers.js new file mode 100644 index 000000000..067c6693a --- /dev/null +++ b/sproutcore/apps/fp/controllers/policies/policies_controllers.js @@ -0,0 +1,76 @@ + +sc_require('models/policy_models'); +sc_require('controllers/scenarios/scenario_controllers'); +sc_require('controllers/controllers'); +sc_require('controllers/tree_content'); +sc_require('controllers/tree_controller'); + +/*** + * + * Organizes the BuiltForms by their tags. + * @type {*|void + */ +Footprint.policyCategoriesTreeContent = Footprint.TreeContent.create({ + + // The content exists in the context of a ConfigEntity. I'm not sure if this matters yet. + configEntityBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.content'), + + // The container object holding nodes + nodeSetBinding: SC.Binding.oneWay('Footprint.policySetActiveController.content'), + // The nodes of the tree + nodesBinding: SC.Binding.oneWay('Footprint.policySetActiveController.policies'), + + // The toOne or toMany property of the node to access the keyObject(s). Here they are Tag instances + keyProperty:'tags', + // The property of the keyObject to use for a name. Here it the 'tag' property of Tag + keyNameProperty:'tag', + + /** + * Query for the keys of the tree + * Query for all tags in the system, to which Policies might associate + * TODO these tags should be limited to those used by Policies + * TODO move to states + */ + keyObjects: function() { + //TODO move + //return Footprint.store.find(SC.Query.local(Footprint.Tag)); + }.property().cacheable() +}); + +Footprint.policyCategoriesTreeController = Footprint.TreeController.create({ + treeItemIsGrouped: YES, + allowsMultipleSelection: NO +}); + +/*** + * The active policy, as dictated by the user's selection + * @type {*} + */ +Footprint.policyActiveController = Footprint.TreeSelectionController.create({ + listController: Footprint.policyCategoriesTreeController +}); + +Footprint.policyEditController = SC.ObjectController.create({ + // Used to create new instances + recordType: Footprint.Policy, + // The bound object controller, which interacts with its content record directly, rather than via a nested store + objectControllerBinding:'Footprint.policyActiveController', + + // Coerce single tag selection into the policy's tags collection + // TODO the view control should support multiple selection + tag: function(propKey, value) { + if (value !== undefined) { + this.get('tags').removeObjects(this.get('tags')); + this.get('tags').pushObject(value); + } + else + return this.get('tags').objectAt(0); + }.property('*content.tags') + +}); + +Footprint.policyControllers = Footprint.ControllerConfiguration.create({ + editController:Footprint.policyEditController, + itemsController:Footprint.policyCategoriesTreeController, + recordSetController:Footprint.policyCategoriesTreeController +}); diff --git a/sproutcore/apps/fp/controllers/policies/policy_sets_controllers.js b/sproutcore/apps/fp/controllers/policies/policy_sets_controllers.js new file mode 100644 index 000000000..6b251fa8a --- /dev/null +++ b/sproutcore/apps/fp/controllers/policies/policy_sets_controllers.js @@ -0,0 +1,56 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +sc_require('controllers/controllers'); +sc_require('controllers/selection_controllers'); +sc_require('controllers/sets_controllers'); + +Footprint.policySetsController = Footprint.SetsController.create({ + listControllerBinding: SC.Binding.oneWay('Footprint.scenariosController'), + property:'policy_sets' +}); + +/*** + * The active policy, as dictated by the user's selection + * @type {*} + */ +Footprint.policySetActiveController = Footprint.ActiveController.create({ + listController:Footprint.policySetsController +}); + + +Footprint.policySetEditController = SC.ObjectController.create({ + // Used to create new instances + recordType: Footprint.Policy, + // The bound object controller, which interacts with its content record directly, rather than via a nested store + objectControllerBinding:'Footprint.policySetActiveController', + + // Coerce single tag selection into the built_forms's tags collection + // TODO the view control should support multiple selection + tag: function(propKey, value) { + if (value !== undefined) { + this.get('tags').removeObjects(this.get('tags')); + this.get('tags').pushObject(value); + } + else + return this.get('tags').objectAt(0); + }.property('*content.tags') +}); + +Footprint.policySetControllers = Footprint.ControllerConfiguration.create({ + editController:Footprint.policySetEditController, + itemsController:Footprint.policySetsController, + recordSetController:Footprint.policySetsController +}); diff --git a/sproutcore/apps/fp/controllers/presentation_controllers.js b/sproutcore/apps/fp/controllers/presentation_controllers.js new file mode 100644 index 000000000..ad2d450c6 --- /dev/null +++ b/sproutcore/apps/fp/controllers/presentation_controllers.js @@ -0,0 +1,211 @@ + + +Footprint.PresentationsController = SC.ArrayController.extend({ +}); + +/*** + * Base class to manage the presentations of a particular type (e.g. maps or results) of matching particular key set in the instantiations + * @type {*} + */ +Footprint.PresentationController = SC.ObjectController.extend({ + + // Set the key to the particular presentation of interest + key: null, + + presentations:null, + presentationsStatus:null, + presentationsStatusBinding:SC.Binding.oneWay('*presentations.status'), + + /** + * Represents the presentation of the bound configEntity that matches the bound key + */ + // This would work if the presentationStatus READY_CLEAN didn't preceed the items being READY_CLEAN + content: function() { + if (this.get('presentations') && (this.getPath('presentationsStatus') & SC.Record.READY)) { + return this.get('presentations').filter(function(presentation, i) { + return presentation.get('key') == this.get('key'); + }, this)[0]; + } + }.property('presentations', 'presentationsStatus', 'key').cacheable(), + + keys: null +}); + +/*** + * Base class to controller the PresentationMedium instances of a particular presentation + * You must set the presentationBinding to bind to a particular presentation + * @type {*} + */ +Footprint.PresentationMediaController = SC.ArrayController.extend(SC.SelectionSupport, { + + orderBy: ['sortPriority DESC', 'name ASC'], + + /** + * The owning Presentation of the PresentationMedium instances + */ + presentation:null, + + /** + * The Presentation's ConfigEntity + */ + configEntity: null, + configEntityBinding: '*presentation.config_entity', + /** + * status needs to be explicitly set to the content.status, because content is a ManyArray + * for some reason the controller delegation of status doesn't "keep up" when content changes + */ + contentStatus: function() { + return this.getPath('content.status'); + }.property('content').cacheable(), + + db_entity_interests: function() { + if (this.get('content') && this.get('contentStatus') & SC.Record.READY) { + return this.get('content').mapProperty('db_entity_interest'); + } + }.property('content', 'contentStatus').cacheable(), + + tables: function() { + if (this.get('content') && this.get('contentStatus') & SC.Record.READY) { + return this.get('content').mapProperty('db_entity_interest.db_entity.table'); + } + }.property('content', 'contentStatus').cacheable(), + + /** + * Groups the db_entity_interests into an SC.Object keyed by unique db_entity key, each valued by an array of one or more DbEntity instances + */ + dbEntityInterestsByKey: function() { + if (this.get('db_entity_interests')) { + return $.mapToCollectionsObject( + this.get('db_entity_interests').toArray(), + function(db_entity_interest) { + return db_entity_interest.getPath('db_entity.key'); + }, + function(db_entity_interest) { return db_entity_interest;}, + function() { return SC.Object.create({ + toString: function() { + return "DbEntityInterestsByKey"; + } + }); + }) + } + }.property('db_entity_interests').cacheable(), + + selectedDbEntityInterestsByKey: function() { + if (this.get('configEntity')) + return this.getPath('configEntity.selections.db_entity_interests'); + }.property('configEntity'), + + /** + * Keeps track of the PresentationMedium instances that are soloing. + */ + _solos:[], + + /** + * Sets the items that are soloing and disables the solos of all other items. This does not change the visible property of other items. It's up to the presenter of the items (e.g. the MapView to use isVisible to see if items are hidden due to their own state or because and item is soloing. + */ + solos: function(propKey, value) { + if (value===undefined) { + return this.get('_solos'); + } + else { + this.get('content').forEach(function(presentationMedium) { + // Disable the solo state of all other items by reverting visibility to the visible value + if (!value.contains(presentationMedium)) { + presentationMedium.set('visibility', presentationMedium.get('applicationVisible')) + } + }, this); + this.set('_solos', value); + } + }.property('content'), + + /*** + * Returns the visibility state of the presentationMedium, depending on if any items are soloing. If the given instance is soloing or no items are soloing and the given instance is not hidden, the function returns YES + * @param presentationMedium + * @returns {*|boolean} + */ + isVisible: function(presentationMedium) { + return this.get('solos').contains(presentationMedium) || (this.get('solos').length == 0 && presentationMedium.get('visibility')==Footprint.VISIBLE); + }, + + // TODO dbEntityInterestsByKey isn't dumping + toString: function() { + return "%@:\n%@".fmt(sc_super(), this.toStringAttributes('configEntity presentation content db_entity_interests selectedDbEntityInterestsByKey'.w())); + } +}); + +/*** + * Aggregates content from the subclasses of the controllers defined above. Assign this to a the content of a + * LibraryController subclass and use that LibraryController for library views + * @type {*} + */ +Footprint.LibraryContent = SC.Object.extend({ + init: function() { + sc_super(); + this.bind('*presentationController.presentation', '.presentation'); + this.bind('*presentationController.keys', '.keys'); + this.bind('*presentationMediaController.content', '.items'); + this.bind('*presentationMediaController.dbEntityInterestsByKey', '.dbEntityInterestsByKey'); + this.bind('*presentationMediaController.selectedDbEntityInterestsByKey', '.selectedDbEntityInterestsByKey'); + }, + + // A Footprint.PresentationController instance that binds a Presentation + presentationController:null, + // A Footprint.PresentationMediaController instance that binds the Presentation's PresentationMedium instances + presentationMediaController:null, + + // The active ConfigEntity + configEntity:null, + // Bound on init to the presentationController.presentation + presentation:null, + + /*** + * All the PresentationMedium instances of the Presentation + */ + items:null, + + /*** + * Bound on init to the presentationMediaController.dbEntityInterestsByKey. This is an SC.Object with attributes + * that are the keys of the DbEntityInterests with array values that are the matching DbEntityInterests + */ + dbEntityInterestsByKey: null, + /*** + * Bound on init to the presentationMediaController.selectedDbEntityInterestsByKey. This is an SC.Object with + * attributes that are the keys of the DbEntityInterests with a single DbEntityInterest for a value that represents + * the one or only DbEntityInterest with that key that is marked selected + */ + selectedDbEntityInterestsByKey: null, + /*** + * All of the keys assigned to all the DbEntityInterests + */ + keys:null, + + tables:null, + /*** + * This is bound to the active Scenario. In the future it should be bound to the more general active ConfigEntity + */ + configEntityBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.content').single() +}); + +/*** + * Base class to extend to bind to a currently active PresentationMedium of a Presentation. + * You must bind content to the list of PresentationMedium instances when subclassing. + * @type {*} + */ +Footprint.PresentationMediumActiveController = Footprint.ActiveController.extend({ +}); + +/*** + * Used for buffered editing of a PresentationMedium + * You must set objectControllerBinding to the PresentationMediumActiveController whose presentationMedium is to be edited + * @type {*} + */ +Footprint.PresentationMediumEditController = SC.ObjectController.extend({ + + // Used to create new instances + recordType: Footprint.PresentationMedium +}); + +Footprint.MediaController = SC.ArrayController.extend(Footprint.ArrayContentSupport, { +}); + + diff --git a/sproutcore/apps/fp/controllers/projects_controllers.js b/sproutcore/apps/fp/controllers/projects_controllers.js new file mode 100644 index 000000000..c82e949d2 --- /dev/null +++ b/sproutcore/apps/fp/controllers/projects_controllers.js @@ -0,0 +1,56 @@ +/* * UrbanFootprint-California (v1.0), Land Use Project Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/config_entity_models'); +sc_require('controllers/regions_controllers'); +sc_require('controllers/controllers'); +sc_require('controllers/selection_controllers'); + +/* + * projectsController binds to a flat list of the Projects in the current context + * @type {*} + */ +Footprint.projectsController = Footprint.ArrayController.create(Footprint.ArrayContentSupport, { + contentBinding:SC.Binding.oneWay('Footprint.regionActiveController.children') +}); + +/*** + * projectSelectionController keeps track of the active Project + * @type {*} + */ +Footprint.projectActiveController = Footprint.ActiveController.create(Footprint.ConfigEntityDelegator, { + parentConfigEntityDelegator: Footprint.regionActiveController, + + listController:Footprint.projectsController +}); + +/*** + * A separate controller from the regionActiveController so that a region can be added or edited without necessarily being the region in context for the rest of the application + * @type {*} + */ +Footprint.projectEditController = SC.ObjectController.create({ + // Used to create new instances + recordType: Footprint.Project, + // The bound object controller, which interacts with its content record directly, rather than via a nested store + objectControllerBinding:SC.Binding.oneWay('Footprint.projectActiveController') +}); + +/*** + * Configuration of controllers for use by edit views + * @type {*} + */ +Footprint.projectControllers = Footprint.ControllerConfiguration.create({ + editController:Footprint.projectEditController, + itemsController:Footprint.projectsController, + recordSetController:Footprint.projectsController +}); diff --git a/sproutcore/apps/fp/controllers/property_controllers.js b/sproutcore/apps/fp/controllers/property_controllers.js new file mode 100644 index 000000000..7d10a04b8 --- /dev/null +++ b/sproutcore/apps/fp/controllers/property_controllers.js @@ -0,0 +1,28 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.PropertiesController = SC.ArrayController.extend({ + recordType: null, + fields: null, + content: function() { + if (this.get('fields')) + return this.get('fields'); + if (this.get('recordType')) + return this.get('recordType').allRecordAttributeProperties(); + + return []; + }.property('fields', 'recordType').cacheable() +}); + diff --git a/sproutcore/apps/fp/controllers/regions_controllers.js b/sproutcore/apps/fp/controllers/regions_controllers.js new file mode 100644 index 000000000..64e440147 --- /dev/null +++ b/sproutcore/apps/fp/controllers/regions_controllers.js @@ -0,0 +1,51 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('controllers/controllers'); +sc_require('controllers/selection_controllers'); +sc_require('controllers/global_config_controllers'); + +/*** + * regionsController organizes the regions as a simple list for region selection and add/remove + * @type {*} + */ +Footprint.regionsController = Footprint.ArrayController.create(Footprint.ArrayContentSupport, { + contentBinding:SC.Binding.oneWay('Footprint.globalConfigController.children') +}); + +/*** + * regionActiveController keeps track of the active Region + * @type {*} + */ +Footprint.regionActiveController = Footprint.ActiveController.create(Footprint.ConfigEntityDelegator, { + listController: Footprint.regionsController, + + // TODO: Work out a way to structure this less hackily. + client: function() { + var clientKey = this.get('key'); + if (!clientKey) return null; + return window['Footprint%@'.fmt(clientKey.capitalize())]; + }.property('content').cacheable() +}); + +/*** + * A separate controller from the regionActiveController so that a region can be added or edited without necessarily being the region in context for the rest of the application + * @type {*} + */ +Footprint.regionEditController = SC.ObjectController.create({ + // Used to create new instances + recordType: Footprint.Region, + // The bound object controller, which interacts with its content record directly, rather than via a nested store + objectControllerBinding:SC.Binding.oneWay('Footprint.regionActiveController') +}); diff --git a/sproutcore/apps/fp/controllers/result_controllers.js b/sproutcore/apps/fp/controllers/result_controllers.js new file mode 100644 index 000000000..137a31f21 --- /dev/null +++ b/sproutcore/apps/fp/controllers/result_controllers.js @@ -0,0 +1,81 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/presentation_models'); +sc_require('controllers/controller_mixins'); +sc_require('controllers/scenarios/scenario_controllers'); +sc_require('controllers/presentation_controllers'); + +Footprint.resultLibrariesController = Footprint.PresentationsController.create({ + contentBinding:SC.Binding.oneWay('Footprint.scenarioActiveController*presentations.results') +}); + +Footprint.resultLibraryActiveController = Footprint.PresentationController.create({ + presentationsBinding:SC.Binding.oneWay('Footprint.resultLibrariesController.content'), + key: 'result_library__default', + keysBinding:SC.Binding.oneWay('.results').transform(function(value) { + if (value && value.get('status') & SC.Record.READY) + return value.mapProperty('db_entity_interest').mapProperty('db_entity').mapProperty('key'); + }) +}); + +/** + * The results of the active resultLibraryActiveController. Results are subclasses of PresentationMedium instances + * @type {*} + */ +Footprint.resultsController = Footprint.PresentationMediaController.create({ + presentationBinding: SC.Binding.oneWay('Footprint.resultLibraryActiveController.content'), + contentBinding: SC.Binding.oneWay('Footprint.resultLibraryActiveController.results') +}); + +/** + * This aggregates the public-facing properties of the other controllers + * The resentationMedium instances for results represent Result instances + * @type {*|void} + */ +Footprint.resultLibraryContent = Footprint.LibraryContent.create({ + presentationController: Footprint.resultLibraryActiveController, + presentationMediaController:Footprint.resultsController +}); + +/*** + * Aggregates the public-facing properties of the other controllers + * @type {*|void} + */ +Footprint.resultLibraryController = SC.ObjectController.create({ + // Binding content makes all other properties accessible via delegation + contentBinding: SC.Binding.oneWay('Footprint.resultLibraryContent') +}); + +/*** + * Binds to the currently selected Result + * @type {*} + */ +Footprint.resultActiveController = Footprint.PresentationMediumActiveController.create({ + listController: Footprint.resultsController +}); + +/*** + * Edits the active Result, a clone of the active Result, or a brand new Result + * @type {*|void} + */ +Footprint.resultEditController = Footprint.PresentationMediumEditController.create({ + objectControllerBinding:'Footprint.resultActiveController' +}); + +Footprint.resultControllers = Footprint.ControllerConfiguration.create({ + editController:Footprint.resultEditController, + itemsController:Footprint.resultsController, + recordSetController:Footprint.resultLibraryController +}); diff --git a/sproutcore/apps/fp/controllers/scenarios/scenario_controllers.js b/sproutcore/apps/fp/controllers/scenarios/scenario_controllers.js new file mode 100644 index 000000000..91748e756 --- /dev/null +++ b/sproutcore/apps/fp/controllers/scenarios/scenario_controllers.js @@ -0,0 +1,94 @@ +/* * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/scenarios_models'); +sc_require('controllers/projects_controllers'); +sc_require('controllers/selection_controllers'); +sc_require('controllers/controllers'); +sc_require('controllers/tree_content'); +sc_require('controllers/tree_controller'); + + +/**** + * The flat version of the active scenarios. This controller sends the events scenariosDidChange + * and scenarioDidChange when the whole set or active scenario is updated + * @type {RecordControllerChangeSupport} + */ +Footprint.scenariosController = Footprint.ArrayController.create(Footprint.RecordControllerChangeSupport, { + contentBinding:SC.Binding.oneWay('Footprint.projectActiveController.children'), + selectionBinding: SC.Binding.oneWay('Footprint.scenarioCategoriesTreeController.selection'), + + selectedItemDidChangeEvent:'scenarioDidChange', + contentDidChangeEvent:'scenariosDidChange' +}); + +/*** + * Represents the active Scenario. This is reset to the first item of Footprint.scenariosController whenever the latter is reset + * @type {*} + */ +Footprint.scenarioActiveController = SC.ObjectController.create(Footprint.ConfigEntityDelegator, { + + contentBinding:SC.Binding.oneWay('Footprint.scenariosController*selection.firstObject'), + parentConfigEntityDelegator: Footprint.projectActiveController, + + // Fetches the category value of the Scenario + category:function() { + return this.getPath('content.categories').filter(function(category) { + return category.key=='category'; + })[0]; + }.property('content').cacheable() +}); + +/*** + * Nested store version of the Scenarios for editing. The selection is bound oneWay to the main controller, so that + * when the main controller selection changes, this one updates its corresponding record + */ +Footprint.scenariosEditController = Footprint.EditArrayController.create({ + allowsEmptySelection:YES, + sourceController: Footprint.scenariosController, + isEditable:YES, + recordType: Footprint.Scenario, + parentEntityKey: 'parent_config_entity', + parentRecordBinding:'Footprint.projectActiveController.content', + nestedStore:null +}); + +Footprint.scenarioCategoriesController = SC.ArrayController.create(Footprint.ArrayContentSupport); + +/*** + * + * Organizes the Scenarios by one of their Category keys. Currently this hard-coded to 'category' but it should be made a property so that the user can categorize Scenarios otherwise + * @type {*|void +*/ +Footprint.scenarioCategoriesTreeController = Footprint.TreeController.create({ + treeItemIsGrouped: YES, + allowsMultipleSelection: NO, + allowsEmptySelection: NO, + content: Footprint.TreeContent.create({ + // Bind to the active project + configEntityBinding: 'Footprint.scenariosController.project', + + nodesBinding: 'Footprint.scenariosController.content', + + // The toOne or toMany property of the node to access the keyObject(s). Here they are Category instances + keyProperty:'categories', + + // The property of the keyObject that access its name, thus the value of each Category of categories + keyNameProperty:'value', + undefinedKeyObject:SC.Object.create({key: 'category', value:'Unknown'}), + + // The unique Category instances assigned to Scenarios that we limited by the special key 'category' + // We'll only show category values of Scenarios that fall within these categories + keyObjectsBinding: 'Footprint.scenarioCategoriesController.content' + }) +}); diff --git a/sproutcore/apps/fp/controllers/selection_controllers.js b/sproutcore/apps/fp/controllers/selection_controllers.js new file mode 100644 index 000000000..5dd6e79cd --- /dev/null +++ b/sproutcore/apps/fp/controllers/selection_controllers.js @@ -0,0 +1,93 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +sc_require('controllers/controllers'); + +Footprint.ActiveController = SC.ObjectController.extend({ + + init: function() { + sc_super(); + // We require from to be set right-off-the bat to avoid problems. We'd have to get rid of this if from were ever bound + if (!this.get('listController')) + throw "'listController' property is null or undefined. Perhaps it wasn't sc_required or was declared below this definition"; + }, + /*** + * Bind to the singleSelection property of the the listController. When the selection changes we update + * our content + */ + contentBinding: SC.Binding.from('.listController*selection.firstObject'), + + observeControllerProperty:null, + + /*** + * An ArrayController or TreeController that has a selection to which we should two-way bind + */ + listController:null, + + /*** + * Determines when the initialContentObserver should fire + * For listControllers, like TreeControllers that can't delegate their status to anything, + * optionally a custom status that determines when to trigger the initialContentObserver + */ + listStatus:function() { + return this.getPath('listController.status'); + }.property('listController'), + + /*** + * This sets it to the first item of listController.selection or failing that the + * first item of listController or list. If content is bound this setting will quickly be undone + * + */ + initialContentObserver:function() { + if (this.get('listStatus') & SC.Record.READY) + this.set('content', this.firstItemOfSelectionSetOrFirstItemOfList()); + }.observes('*listController.status'), + + + /*** + * Since our controllers currently only support one active item, take the first item of the selection set, or + * the first item of the list if nothing is selected + * @returns {*|Object|Object|Object|Object} + */ + firstItemOfSelectionSetOrFirstItemOfList: function() { + return this.getPath('listController.selection').length() > 0 ? + this.getPath('listController.selection.firstObject') : + this.firstItem() + }, + /*** + * The first item of the listController if nothing is selected. Override this for TreeControllers, etc + * @returns {*|Object|Object|Object|Object} + */ + firstItem: function() { + return this.getPath('listController.firstObject'); + }, + + + toString: function() { + return this.toStringAttributes('content listController listStatus'.w()); + } + +}); + +Footprint.TreeSelectionController = Footprint.ActiveController.extend({ + /*** + * Specifies how to fetch the first node of the TreeController items + * @returns {*} + */ + firstItem: function() { + return this.getPath('listController.nodes.firstObject'); + } +}); + diff --git a/sproutcore/apps/fp/controllers/sets_controllers.js b/sproutcore/apps/fp/controllers/sets_controllers.js new file mode 100644 index 000000000..77cae21b7 --- /dev/null +++ b/sproutcore/apps/fp/controllers/sets_controllers.js @@ -0,0 +1,74 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +sc_require('controllers/controllers'); +/** + * Manages a configEntity set, such as 'policy_sets' or 'built_form_sets' by storing the array of sets and setting its initial selection to the configEntity's selection + * Mixes in SingleSelectionSupport, which updates the singleSelection property when the selection change, and updates the selection when the singleSelection property is set. When the singleSelection is update it in turn updates the configEntity's set + * + * This is a 2-way binding. 1) The ConfigEntity's specific selections.set property, 2) The ArrayController selection, which is simplified with the property singleSelection. + * @type {Class} + */ +Footprint.SetsController = SC.ArrayController.extend(Footprint.SingleSelectionSupport, Footprint.ArrayContentSupport, { + allowsEmptySelection:NO, + listController: null, + property:null, + + configEntity:null, + configEntityBinding:SC.Binding.oneWay('*listController.selection.firstObject'), + configEntityStatus:null, + configEntityStatusBinding:SC.Binding.oneWay('*configEntity.status'), + + configEntitySet:function() { + return this.getPath('configEntity.%@'.fmt(this.get('property'))); + }.property('configEntityStatus', 'property').cacheable(), + + contentBinding:SC.Binding.oneWay('.configEntitySet'), + + selectionPath:function() { + return 'configEntity.selections.sets.%@'.fmt(this.get('property')); + }.property('configEntity', 'property').cacheable(), + + contentObserver: function() { + if (this.getPath('configEntity.status')===SC.Record.READY_CLEAN) { + this.updateSelectionAfterContentChange(); + this.set('singleSelection', this.get('configEntitySelection')); + } + }.observes('*configEntity.status').cacheable(), + + /* + * Property to read and update the value referenced by the selectionPath + * The selection sj + */ + configEntitySelection: function(keyProp, value) { + if (value !== undefined) { + // Update the configEntity selection. Note that this will make the ConfigEntity dirty + // Don't do this. We don't save it anyway. Selections should be per-user anyway + //this.setPath(this.get('selectionPath'), value) + } + return this.getPath(this.get('selectionPath')); + }.property('configEntity', 'selectionPath'), + + /* + * When singleSelection is updated via set, we update the configEntitySelection property + */ + singleSelectionObserver: function() { + this.set('configEntitySelection', this.get('singleSelection')); + }.observes('.singleSelection'), + + toString: function() { + return this.toStringAttributes('content configEntity property selectionPath configEntitySelection'.w()); + } +}); diff --git a/sproutcore/apps/fp/controllers/synced_selection_controller.js b/sproutcore/apps/fp/controllers/synced_selection_controller.js new file mode 100644 index 000000000..5edd8a6eb --- /dev/null +++ b/sproutcore/apps/fp/controllers/synced_selection_controller.js @@ -0,0 +1,49 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +/*** + * A selection controller whose contents is a list of selectable values. Zero or one items in the list are selected, + * depending on the property values of the editController.content indicated by the property at propertyKey. If all + * of editController's content property values are identical, then that single identical item will be the selection + * of this controller. If the controller's selection is updated to a new value or no value, all the editController's + * content property values will be updated accordingly. If not all values are identical then there is no selected item. + * + */ + +sc_require('controllers/controllers.js'); + +Footprint.SyncedSelectionController = SC.ArrayController.extend(Footprint.SingleSelectionSupport, { + + /*** + * Set this to the controller whose content items are being bulk viewed or edited + */ + editController:null, + editControllerContent:null, + editControllerContentBinding:SC.Binding.oneWay('.editController.content'), + + propKey:null, + + /*** + * Whenever the content changes select an item of the ArrayController if the content all have the same + * property value + */ + editControllerObserver: function() { + if (this.get('editControllerContent').mapProperty(this.get('propKey')).uniq().length==1) + this.selectObject(this.get('editControllerContent').firstObject().get(this.get('propKey'))); + else + this.deselectObjects(this.get('selection')); + }.observes('editControllerContent') +}); + diff --git a/sproutcore/apps/fp/controllers/tool_controllers.js b/sproutcore/apps/fp/controllers/tool_controllers.js new file mode 100644 index 000000000..b477a61df --- /dev/null +++ b/sproutcore/apps/fp/controllers/tool_controllers.js @@ -0,0 +1,43 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +/*** + * Controls the use of map tools by enabling or disabling them depending on the application state + * @type {* +*/ +Footprint.toolController = SC.Object.create({ + /*** + * Controls if navigation tools are enabled + */ + navigatorIsEnabled: YES, + /*** + * Controls if selection tools are enabled + */ + selectorIsEnabled: NO, + /*** + * Controls if feature edit/info tools are enabled + */ + featurerIsEnabled: NO, + + /*** + * Set true whenever the selector needs refresh, which clears the box or other shape it makes + */ + selectionToolNeedsReset: NO +}); + +Footprint.paintingController = SC.Object.create({ + developmentPercent: 1, + densityPercent: 1, + isFullRedevelopment: NO +}); diff --git a/sproutcore/apps/fp/controllers/tree_content.js b/sproutcore/apps/fp/controllers/tree_content.js new file mode 100644 index 000000000..44897149d --- /dev/null +++ b/sproutcore/apps/fp/controllers/tree_content.js @@ -0,0 +1,175 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +/*** + * Used by the tree controller to create a hierarchy + * @type {*} + */ +Footprint.TreeItem = SC.Object.extend({ + keyObject:null, + nodes:null, + // The property o the keyObject that is its viewable name + keyNameProperty:null, + nodeSet:null, + + name:function() { + return this.get('keyObject').getPath(this.get('keyNameProperty')); + }.property('keyNameProperty'), + + treeItemIsExpanded: YES, + treeItemChildren: function(){ + return this.get('nodes'); + }.property('nodes') +}); + +/** + * Objectifies the relationship between top level items and second level items (and possibly deeper) + * The data structure is {key_string1: {key: top_level_instance, values: second_level_instances}, key_string2: ...} where key_string1 is a string version of the top_level_instance so that the values can be grouped under a single key. + */ +Footprint.TreeContent = SC.Object.extend({ + /*** + * Subclasses must set or bind the following properties, unless the derivitive properties are set, as described: + */ + + // The configEntity that is in scope. This will update all the controller properties whenever set or reset + configEntity: null, + // The configEntity property path to access the nodeSet that contains the nodes + // Leave null if their is no set class and/or you set the nodes property directly + keyProperty:null, + // The property of the keyObject that access its name + keyNameProperty:null, + + init: function() { + sc_super(); + if (this.get('nodeSetProperty')) + this.bind('nodeSet', '*configEntity.%@'.fmt(this.get('nodeSetProperty'))); + if (this.get('nodesProperty')) + this.bind('nodes', '*nodeSet.%@'.fmt(this.get('nodesProperty'))); + }, + + // Start the tree expanded + treeItemIsExpanded: YES, + // The name of the root element + name: "root", + // Set to the configEntity's nodeSetProperty whenever the confgEntity is updated, or can be set/bound directly instead + nodeSet: null, + + // The nodes of the tree. Set the nodesProperty or bind nodes + nodes:null, + nodesStatus:null, + nodesStatusBinding:SC.Binding.oneWay('*nodes.status'), + + /*** + * The unique key objects used by the the TreeController. These are model instances that have a string property that label the top-level tree nodes + * These are only used to populate a select view that lets a user assign an existing key to a node. + */ + keyObjects: null, + keyObjectsStatus:null, + keyObjectsStatusBinding: '*keyObjects.status', + + /*** + * Default sorting properties for the nodeSet level of tree controllers + */ + nodeSetSortProperties: ['name'], + /*** + * Dict with key: YES for any sorting key that should be reversed + */ + reverseNodeSetSortDict: null, + + /*** + * Default sorting properties for the node level of tree controllers + */ + sortProperties:['name'], + /*** + * Dict with key: YES for any sorting key that should be reversed + */ + reverseSortDict: null, + + /*** + * A Default key object to use if no keys are found for a node. + * If not specified unmatched nodes will not appear in the tree + */ + undefinedKeyObject: null, + + /*** + * Creates a object whose attributes are the top-level tree key names and values are the nodes with that key + */ + tree:function() { + var self = this; + return $.mapToCollectionsObjectWithObjectKeys( + self.get('nodes') || [], + function(node) { // create 'keys' attributes + var list = arrayOrItemToArray(node.getPath(self.get('keyProperty'))).filter( + function(key) { + // only accept the key objects that match keyObjects + // This allows us to filter out keyObjects of the nodes that we don't care about + // For instance, with Scenarios we only care about Category instances whose key property is + // 'category' + return this.get('keyObjects').contains(key); + }, + self + ); + return list.length > 0 ? list : arrayOrItemToArray(self.get('undefinedKeyObject')); + }, + function(node) { // create 'values' attributes + return node; + }, + function(keyObject) { // stringify keys + return keyObject.getPath(self.get('keyNameProperty')); + }); + }.property('nodesStatus', 'nodes', 'keyProperty', 'keyObjects', 'keyObjectsStatus').cacheable(), + + observeNodes: function() { + this.invokeNext(function() { + this.notifyPropertyChange('tree'); + }); + }.observes('.nodes.[]'), + + /*** + * This is the flattened version of tree which is actually used by the View. It contains a list of TreeItem instances + * that each hold the top-level instance in the keyObject. These might be Categories, Tags, etc. The nodes are the + * second-tier instances, such as Scenarios or BuiltForms + */ + treeItemChildren: function() { + var self = this; + if (this.get('tree')) { + var items = $.map(this.get('tree'), function(entry, keyString) { + var values = entry.values; + values.sortPropertyPath(self.get('sortProperties'), self.get('reverseSortDict')); + return Footprint.TreeItem.create({ + nodeSet: self.get('nodeSet'), + keyObject: entry.key, + nodes: values, + keyNameProperty: self.get('keyNameProperty') + }); + }) + return items.sortPropertyPath(this.get('nodeSetSortProperties'), self.get('reverseNodeSetSortDict')); + } + return null; + }.property('tree').cacheable(), + + sortedNodes: function() { + var treeItemChildren = this.get('treeItemChildren'); + if (treeItemChildren) { + return $.shallowFlatten(treeItemChildren.map(function(treeItem) { return treeItem.get('nodes') })); + } + //if (this.get('arrangedObjects')) + // return this.get('arrangedObjects').filter(function(obj) { return !obj.instanceOf(Footprint.TreeItem)}); + }.property('treeItemChildren').cacheable(), + + toString: function() { + return "%@:\n%@".fmt(sc_super(), this.toStringAttributes('nodeSetProperty nodeSet nodes keyProperty keyObjects keyNameProperty tree treeItemChildren'.w())); + } +}); diff --git a/sproutcore/apps/fp/controllers/tree_controller.js b/sproutcore/apps/fp/controllers/tree_controller.js new file mode 100644 index 000000000..86489a7cb --- /dev/null +++ b/sproutcore/apps/fp/controllers/tree_controller.js @@ -0,0 +1,142 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +sc_require('controllers/controllers'); + + /*** + * Specialized TreeController that takes a Footprint.TreeContent for its ontent + * @type {Class} + */ +Footprint.TreeController = SC.TreeController.extend(Footprint.SingleSelectionSupport, SC.CollectionViewDelegate, { + treeItemIsGrouped: YES, + allowsMultipleSelection: NO, + allowsEmptySelection: NO, + + // Delegate the status to the nodes property of the content to get a status + // The status is used by Footprint.SelectController to know they can assign their content to the selected item + // or else first item of the nodes list + statusBinding:SC.Binding.oneWay('*nodes.status'), + + /*** + * Returns the record type + */ + recordType: function() { + return this.getPath('nodes.firstObject.constructor'); + }.property('.nodes'), + + // .......................................................... + // DRAG SOURCE SUPPORT + // + + /** + When dragging, add Task data type to the drag. + */ + /*collectionViewDragDataTypes: function(view) { + return [this.get('recordType'), Footprint.TreeItem]; + },*/ + + /** + If the requested dataType is a Task, provide the currently selected tasks. Otherwise return null. + */ +/* collectionViewDragDataForType: function(view, drag, dataType) { + var ret=null, sel; + *//* + if (dataType === CoreTasks.Task) { + sel = view.get('selection'); + ret = []; + if (sel) sel.forEach(function(x) { ret.push(x); }, this); + } + *//* + return ret; + },*/ + + // .......................................................... + // DROP TARGET SUPPORT + // + /*collectionViewComputeDragOperations: function(view, drag, proposedDragOperations) { + // TODO implement drag support fully + return SC.DRAG_NONE; + if (drag.hasDataType(this.get('recordType'))) { + return SC.DRAG_MOVE; + } + if (drag.hasDataType(Footprint.TreeItem)) { + return SC.DRAG_MOVE; + } + else { + return SC.DRAG_NONE; + } + },*/ + + /** + Called if the user actually drops on the view. Since we are dragging to and from + the same view, let the CollectionView handle the actual reorder by returning SC.DRAG_NONE. + If the drop target is the first index (before the unassign branch) do nothing by returning + SC.DRAG_MOVE. + */ + /*collectionViewPerformDragOperation: function(view, drag, dragOp, idx, dropOp) { + + var ret = SC.DRAG_MOVE; + + // tells the CollectionView to do nothing + if (idx < 0) + return ret; + + // Extract tasks to drag + var records = drag.dataForType(this.get('recordType')); + if(!records) + return ret; + + // Get assignee of item before drop location + var content = view.get('content'); + var targetAssignee = content.objectAt(idx).get('assignee'); + + // Set dragged records' assignee to new assignee + records.forEach(function(record) { + if (record.get('assignee') !== targetAssignee) { + var targetAssigneeId = targetAssignee === null ? + null : + targetAssignee.get('id'); + console.log('Reassigning record "' + record.get('name') + '" to: ' + (targetAssignee? targetAssignee.get('name') : 'Unassigned')); + record.set('assigneeId', targetAssigneeId); + ret = SC.DRAG_NONE; + } + }, this); + + if (ret === SC.DRAG_NONE) { + // Save changes here. Saving ordering will probably be related to the user's database data + } + + return ret; + },*/ + + /** + Called by the collection view to delete the selected items. + + @param {SC.CollectionView} view collection view + @param {SC.IndexSet} indexes the items to delete + @returns {Boolean} YES if the deletion was a success. + */ +/* collectionViewDeleteContent: function(view, content, indexes) { + if (content && (SC.typeOf(content.destroyAt) === SC.T_FUNCTION || SC.typeOf(content.removeAt) === SC.T_FUNCTION)) { + // Do the remove + return YES; + } + return NO; + },*/ + + toString: function() { + return this.toStringAttributes('content'.w()); + } +}); diff --git a/sproutcore/apps/fp/controllers/user_controller.js b/sproutcore/apps/fp/controllers/user_controller.js new file mode 100644 index 000000000..d1aa50a71 --- /dev/null +++ b/sproutcore/apps/fp/controllers/user_controller.js @@ -0,0 +1,35 @@ +// ========================================================================== +// Project: Footprint.userController +// Copyright: @2012 My Company, Inc. +// ========================================================================== +/*globals Footprint */ + +/*** + * User controller expects a single item list or single Footprint.User record set to its content property + * @extends {SC.ArrayController} + */ +Footprint.userController = SC.ArrayController.create(Footprint.ArrayContentSupport, { + setCookie: function(duration) { + var cookie = this.findCookie() || + SC.Cookie.create({ + name: 'user.api_key', + value: Footprint.userController.getPath('firstObject.api_key') + }); + if (duration) { + var d = new Date(); + d.setTime(d.getTime() + duration); + cookie.expires = d; + } + else + cookie.expires = null; + cookie.write(); + }, + destroyCookie: function() { + var cookie = this.findCookie(); + if (cookie) + cookie.destroy() + }, + findCookie: function() { + return SC.Cookie.find('user.api_key'); + } +}); diff --git a/sproutcore/apps/fp/core.js b/sproutcore/apps/fp/core.js new file mode 100644 index 000000000..da322cbca --- /dev/null +++ b/sproutcore/apps/fp/core.js @@ -0,0 +1,2 @@ + +Footprint.STATIC = sc_static('images/loading.png').replace('images/loading.png','%@'); \ No newline at end of file diff --git a/sproutcore/apps/fp/data_sources/data_source.js b/sproutcore/apps/fp/data_sources/data_source.js new file mode 100644 index 000000000..45dee7369 --- /dev/null +++ b/sproutcore/apps/fp/data_sources/data_source.js @@ -0,0 +1,684 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('data_sources/server_api_caller'); + +Footprint.DataSource = SC.DataSource.extend({ + + init: function() { + this._storeKeysToRetrieve = []; + this._storeForRetrieve = null; + sc_super(); + }, + + fetch: function(store, query, retry) { + + // Return local queries as handled without doing anything. We always assume that the data needed by a loca + // query was already fetched by a remote query. + if (!query.get('isRemote')) { + store.dataSourceDidFetchQuery(query); + return YES; + } + + var recordType = query.recordType; + + // For some reason the generic SC.Record type is sometimes fetched. We obviously can't handle it here + if (recordType == Footprint.Record) { + logError("Received a Footprint.Record record type. This should never happen"); + return NO; + } + + // If we are doing a set GET but have no ids, don't query + if (query.parameters && query.parameters.ids && query.parameters.ids.length == 0) { + logError("Received an ids parameters with no ids. This probably shouldn't happen."); + store.dataSourceDidFetchQuery(query); + return YES; + } + + // Use the recordType and parameters to form the API call + var apiCaller = this.recordTypeToApiCaller(recordType, query.parameters); + if (apiCaller) { + apiCaller.load( + this, + this._didFetch, + { query: query, store: store, recordType: recordType, retry:YES } + ); + } + // For now if no handler exists, it means we have an internal Sproutcore record type + return YES; + }, + + _didFetch: function(response, params) { + var store = params.store; + var query = params.query; + var recordType = params.recordType; + var storeKeys; + // NOTE: If we get a response that's 200 OK but has a string response body, we're assuming + // that it's this odd edge case where selecting too many parcels bombs out the server but + // returns a 200. + if (SC.$ok(response) && SC.typeOf(response.get('body')) === SC.T_HASH) { + if (response.get('body')['objects']) { + // If the API returned a list there will be an 'objects' property containing all the object + var self = this; + var objs = $.map(response.get('body')['objects'], function(obj) { + return self._transformTastypieJsonToSproutcore(obj, recordType); + }); + storeKeys = store.loadRecords(recordType, objs); + } + else { + // Otherwise return the single result as a list (loadRecord might work here instead) + var obj = this._transformTastypieJsonToSproutcore(response.get('body'), recordType); + storeKeys = [store.loadRecord(recordType, obj)]; + } + store.dataSourceDidFetchQuery(query, storeKeys); + } + else { + // handle special weird error case where nginx returns a 200 OK with an error message in + // the body. + if (SC.$ok(response) && SC.typeOf(response.get('body')) !== SC.T_HASH) + store.dataSourceDidErrorQuery(query, response); //same as below for now - may require special handling later + // handle error case where we want to retry + // TODO: This needs to be smarter about infinite-loop retrying certain things (e.g. 500 + // server errors). + else if (!params.retry) + this.fetch(store, query, YES); + // handle regular error case + else + store.dataSourceDidErrorQuery(query, response); + } + + return YES + }, + + /*** + * Transform the Tastypie model object to a Sproutcore formatted one, which for now just means using an id for related items instead of a uri + * @param obj + * @param recordType: Optional recordType to inform transformation + * @return {*} + * @private + */ + _transformTastypieJsonToSproutcore: function(obj, recordType) { + return this._transform(this._deurlify, obj, null, null, recordType); + }, + + _transform: function(func, obj, parent, key, recordType) { + var self = this + if (typeof obj === 'object') { + if (obj instanceof Array) { + return obj.map(function(value) { + return self._transform(func, value, parent, key, recordType); + }) + } + else { + return obj && + // Certain recordTypes have child dictionaries that transformed to SC.Objects. + // We don't want to transform the recordType objects themselves, just their keys that are dicts, hence key + (key && [Footprint.BuiltForm, Footprint.LayerSelection].contains(recordType) ? + mapObjectToSCObject(obj, function(key, value) { + return [key, self._transform(func, value, obj, key, recordType)]; + }) : + $.mapObjectToObject(obj, function(key, value) { + return [key, self._transform(func, value, obj, key, recordType)]; + })); + } + } + else { + return func.apply(this, [obj, parent, key]); + } + }, + /** + Transforms the association fields from Resource URI django-tastypie format to the Sproutcore related id format + */ + _deurlify: function(value, parent, key) { + if (typeof value === "string" && value.indexOf('/') === 0) { + parent['_footprint_%@'.fmt(key)] = value; + return this._convertUrlToId(value); + } + else + return value; + }, + _convertUrlToId: function(value) { + return parseInt(value.split('/').reverse()[1]); + }, + + /*** + * Transform the Sproutcore model object to a Tastypie formatted one, which for now just means using a resource url for related items instead of an id + * @param obj + * @return {*} + * @private + */ + _transformSproutcoreJsonToTastypie: function(store, obj, originalObj, recordType) { + var self = this; + if (!obj) { + return obj; + } + else if (typeof obj === 'object') { + if (obj.isEnumerable) { + return obj.map(function(value) { + // Extract the attributes from the record if it is a record + var dataHash = value.storeKey ? store.readDataHash(value.storeKey) : value; + // Get the parent store data hash so that we only send values that have actually changed + var originalDataHash = store.parentStore && value && value.storeKey ? + this._processDataHash(store.parentStore.readDataHash(value.storeKey), data.recordType) : + null; + return self._transformSproutcoreJsonToTastypie(store, dataHash, originalDataHash, recordType) + }); + } + else { + var modifiedObject = obj && remove_keys( + remove_keys_matching_object(obj, SC.Object.create()), + $.map(obj, function(v,k) { return k[0]==('_') ? k : null; }).compact()); + + return obj && $.mapObjectToObject(modifiedObject, function(key, value) { + // Change the 'resource_uri' property from an id to a uri. This probably doesn't matter to the API + if (key=='resource_uri') { + return [key, self._urlify(value, recordType)]; + } + else { + // If we can detect that the primitive value has not changed, leave it out of the transformation + // This minimizes the data that is sent to the API in a PATCH operation. + // TODO we probably need to limit this to PATCHES. POST and PUT probably won't like missing values + // TODO This is causing problems on delete, or on corrupt data, so omitting for now + //if (value && !value.storeKey && originalObj && value===originalObj[key]) + // return null; + + // Extract the attributes from the record if it is a record + var data = value && value.storeKey ? store.readDataHash(value.storeKey) : value; + // Get the parent store data hash so that we only send values that have actually changed + var originalData = store.parentStore && value && value.storeKey ? + this._processDataHash(store.parentStore.readDataHash(value.storeKey), data.recordType) : + null; + var childRecordType = self._modelClassOfAttribute(recordType, key); + return [key, self._transformSproutcoreJsonToTastypie(store, data, originalData, childRecordType)]; + } + }); + } + } + else { + // Change the id to a resource uri if a recordType is defined + return this._urlify(obj, recordType); + } + }, + + /** + * Returns the child record type for the RecordAttribute that the key represents, if any + * @param recordType - the recordType that owns the key. If this is null the function returns null + * @param key - the key of the recordType + * @returns {*} The record type of the attribute or null + * @private + */ + _modelClassOfAttribute: function(recordType, key) { + if (recordType) { + var attribute = recordType.prototype[key]; + if (attribute && + attribute.kindOf && + attribute.kindOf(SC.RecordAttribute)) { + + return (typeof attribute.type == 'string' || attribute.type.kindOf) ? eval(attribute.type) : null; + } + else if (recordType.prototype.resolveAttributeType) { + return recordType.prototype.resolveAttributeType(key) + } + } + else + return null; + }, + + /** + Transforms the association fields from and id to Resource URI django-tastypie format + */ + _urlify: function(value, recordType) { + if (recordType) { + // Detect negative ids, indicating a new record and transform to 0 for tastypie + // Make sure that the apiRecordType is called to get base classes where needed. + return "/footprint/api/v1/%@/%@/".fmt(this.toApiResourceName(recordType.apiRecordType()), value < 0 ? 0 : value); + } + else + return value; + }, + + /** + * Since SC.Store calls once on each storeKey, we accumulate and then invoke once at the end of the run loop + * @param store + * @param storeKeys + */ + retrieveRecords: function(store, storeKeys) { + + var recordTypes = storeKeys.map(function(storeKey) { + return SC.Store.recordTypeFor(storeKey); + }); + + // Never load ConfigEntities with retrieveRecords. + // It causes a problem when a Feature is being fetched that has a config_entity_id + // because the store tries to fetch the ConfigEntity while something else is busy_loading + if (recordTypes.contains(Footprint.ConfigEntity)) + return NO; + + storeKeys.forEach(function(storeKey) { + this._storeKeysToRetrieve.push(storeKey); + }, this); + // We assume this is always the same store + this._storeForRetrieve = store; + this.invokeOnce('_retrieveRecords'); + return YES; + }, + + _retrieveRecords: function() { + var storeKeys = this._storeKeysToRetrieve; + var store = this._storeForRetrieve; + + // convert storeKeys into id’s sorted by recordType. + var recordTypes = SC.Set.create(), // to store record types. + sortedIds = {}, + ret=null ; // return value + + storeKeys.forEach(function(storeKey) { + var recordType = SC.Store.recordTypeFor(storeKey); + recordTypes.add(recordType); + + var typeGuid = SC.guidFor(recordType); + var ids = sortedIds[typeGuid]; + if (!ids) ids = sortedIds[typeGuid] = []; + + // map storeKey to ID + var id = store.idFor(storeKey); + ids.push(id); + + }, this); + + // now for each recordType, initiate a request + recordTypes.forEach(function(recordType) { + var ids = sortedIds[SC.guidFor(recordType)]; + var local_ret = this.retrieveRecordsOfType(store, recordType, ids, ret); + // Set SC.MIXED_STATE if we get different YES/NO across types + ret = (ret === NO && local_ret) ? SC.MIXED_STATE : (ret === null ? local_ret : ret); + }, this); + this._storeKeysToRetrieve = []; + + return ret; + }, + + retrieveRecordsOfType: function(store, recordType, ids, retry) { + if (ids.length > 0) { + var apiCaller = this.recordTypeToApiCaller(recordType, {ids: ids}); + // if apiCaller was found - initiate request + if (apiCaller) { + apiCaller.load( + this, + this._didRetrieveRecords, + { store: store, recordType: recordType, ids: ids, retry:retry }); + return YES; + } + } + }, + + // Called when a group of records have returns. assume result is array of data hashes + // Also used for updates and creates that need to update the record/s that was/were saved + _didRetrieveRecords: function(response, params) { + var store = params.store, + recordType = params.recordType; + + // normal: load into store...response == dataHash + if (SC.$ok(response)) { + if (!response.get('body')['objects']) { + logError('Response body has no objects!'); + } + else { + // If the API returned a list there will be an 'objects' property containing all the object + var self = this; + var objs = response.get('body')['objects'].map(function(obj) { + // TODO this shouldn't be needed anymore, just make features a non nested property + return self._transformTastypieJsonToSproutcore(obj, recordType); + }); + if (params.create) + params.storeKeys.forEach(function(storeKey, i) { + var object = objs[i]; + params.store.dataSourceDidComplete(storeKey, object, object.id); + }); + else { + if (params.ids) { + params.ids.forEach(function(id) { + if (!(store.find(recordType, id).get('status') & SC.Record.BUSY)) { + logWarning('id %@ of recordType: %@ is not BUSY!'.fmt(id, recordType)); + }; + }); + } + else { + params.storeKeys.forEach(function(storeKey) { + if (!(store.peekStatus(storeKey) & SC.Record.BUSY)) { + logWarning('storeKey %@ of recordType: %@ is not READY_CLEAN!'.fmt(storeKey, recordType)); + } + }); + } + var storeKeys = store.loadRecords(recordType, objs || [], objs.mapProperty(recordType.prototype.primaryKey)); + storeKeys.forEach(function(storeKey) { + if (store.peekStatus(storeKey) !== SC.Record.READY_CLEAN) { + logWarning('storeKey %@ of recordType: %@ is not READY_CLEAN!'.fmt(storeKey, recordType)); + } + }); + } + } + // error: indicate as such...response == error + //TODO storeKey expected but this ran on multiple storeKeys + } else { + //store.dataSourceDidError(storeKey, response.get('body')); + if (!params.retry) { + logWarning('DataSource did error for recordType: %@. Response: %@. Retrying'.fmt(recordType, response.get('body'))); + this.retrieveRecordsOfType(store, recordType, params.ids, YES); + } + else + logError('DataSource did error for recordType: %@. Response: %@'.fmt(recordType, response.get('body'))); + + } + }, + + /*** + * Get the create uri for uploading files. + * @param store + * @param storeKey + * @returns {*} + */ + uploadUri: function(store, storeKey) { + // Strip the api url to make it a django call + return this.createRecord(store, storeKey, YES).replace( + /api\/v\d+\/db_entity\//, 'upload/'); + }, + + /*** + * Creates a new record. + * @param store + * @param storeKey + * @param uriOnly: Set to YES to return the url without making the server call. This is + * for the file upload hack + * @returns {*} + */ + createRecord: function(store, storeKey, uriOnly) { + + var recordType = store.recordTypeFor(storeKey); + + if (recordType==Footprint.User) { + // We don't allow creating users at the moment, and we don't want create to trigger an API call + store.dataSourceDidComplete(storeKey, null, store.idFor(storeKey)); + return YES + } + + // Hack for Scenario type + // TODO Sproutcore should be able to handle subclasses mixed in a list, but right + // now our Scenarios are all the base class Footprint.Scenario + if (recordType==Footprint.Scenario) { + var scenario = store.materializeRecord(storeKey) + recordType = scenario.getPath('origin_config_entity.categories.firstObject.value') == 'Future' ? Footprint.FutureScenario : Footprint.BaseScenario; + } + + var apiCaller = this.recordTypeToApiCaller(recordType, {}, 'POST'); + if (uriOnly) + return apiCaller.uri; + + // Create is always performed as a PATCH, so convert this to a multi-object request with storeKeys + apiCaller.create( + this, + this._didCreate, + { store: store, storeKeys: [storeKey], recordType: recordType } + ); + + return YES; + }, + + + updateRecords: function(store, storeKeys) { + + var recordType = store.recordTypeFor(storeKeys[0]); + + var apiCaller = this.recordTypeToApiCaller( + recordType, {config_entity:Footprint.scenarioActiveController.get('content')}, 'PATCH'); + storeKeys.forEach(function(storeKey) { + var status = store.readStatus(storeKey); + if (!(status & SC.Record.BUSY)) { + logError("About to update %@ record that is not BUSY! storeKey: %@, status %@".fmt(recordType, storeKey, getStatusString(status))); + } + else { + logInfo("About to update %@ record with storeKey: %@, status %@".fmt(recordType, storeKey, getStatusString(status))); + } + }); + apiCaller.update( + this, + this._didUpdate, + { store: store, storeKeys: storeKeys, recordType: recordType } + ); + + return YES; + }, + updateRecord: function(store, storeKey) { + var recordType = store.recordTypeFor(storeKey); + + var apiCaller = this.recordTypeToApiCaller( + recordType, + {id:store.idFor(storeKey), config_entity:Footprint.scenarioActiveController.get('content')}, + 'PATCH'); + apiCaller.update( + this, + this._didUpdate, + { store: store, storeKey: storeKey, recordType: recordType } + ); + + return YES; + }, + + + /*** + * Called upon completing the API call to create the record. + * @param response + * @param store + * @param storeKey + * @private + */ + _didCreate: function(response, params) { + this._didSave(response, params, YES); + }, + + _didUpdate: function(response, params) { + this._didSave(response, params, NO); + }, + + _didSave: function(response, params, create) { + if (SC.ok(response)) { + // Check if the store is destroyed by a subsequent update. If so do nothing return YES. + if (params.store.get('isDestroyed')) + return YES; + + // Single update cases + if (params.storeKey) { + var status = params.store.readStatus(storeKey); + + // Some temporary state error handling + if (!(status & SC.Record.BUSY)) { + logError("After save %@ record is not BUSY! This should never happen. storeKey: %@, status: %@".fmt(params.recordType, params.storeKey, getStatusString(status))); + } + else + if (create) + params.store.dataSourceDidComplete(params.storeKey, response.get('body'), response.getPath('body.id')); + else + params.store.dataSourceDidComplete(params.storeKey); + } + // Multiple save cases + else { + // Save that returned a response + if (response.get('body')['objects']) + // Handle Record types that need to return the records upon saving + this._didRetrieveRecords(response, $.extend({}, params, {create:create})); + // Update without a response + else { + params.storeKeys.forEach(function(storeKey) { + params.store.dataSourceDidComplete(storeKey); + }); + } + } + } else { + if (params.storeKey) { + params.store.dataSourceDidError(params.storeKey); + } + else { + params.storeKeys.forEach(function(storeKey) { + params.store.dataSourceDidError(storeKey, response.get('errorObject')); + }); + } + } + }, + + destroyRecord: function(store, storeKey) { + if (SC.kindOf(store.recordTypeFor(storeKey), Todos.Task)) { + SC.Request.deleteUrl(store.idFor(storeKey)).header({ + 'Accept': 'application/json' + }).json() + .notify(this, this.didDestroyTask, store, storeKey) + .send(); + return YES; + + } else return NO; + }, + didDestroyTask: function(response, store, storeKey) { + if (SC.ok(response)) { + store.dataSourceDidDestroy(storeKey); + } else store.dataSourceDidError(response); + }, + + /*** + * Resolves the API name of the record based on its type. Record classes can override apiClassName to provide + * a custom name, otherwise the name will be inferred by the class name + * @param recordType + * @returns {*} + */ + toApiResourceName: function(recordType) { + // Some record types have this attribute so that they send a base class name to the server + var useRecordTypeName = recordType.apiClassName() || recordType.toString().split('.')[1].decamelize(); + return useRecordTypeName; + }, + + /*** + * Constructs a relative URI for proxying to the Django server + * TODO set up a non-proxy option for the production environment + * @param apiModelName + * @param id + * @return {*} + * @private + */ + _constructUri : function(apiModelName, options) { + // Append the id as 'id/' or ids in the form: 'set/id1;id2;....' or nothing + var idSegment = options['id'] ? '%@/'.fmt(options['id']) : (options['ids'] ? 'set/%@/'.fmt(options['ids'].join(';')): ''); + return '/footprint/api/v1/%@/%@'.fmt(apiModelName, idSegment); + }, + + authenticationApiCaller: function(username, password) { + // TODO this just uses a Django view instead of the api + uri = '/fp/api_authentication'; + var uriOptions = {format:'json', username:username, password:password}; + return this.createCallerForUri(uri, uriOptions, Footprint.Use); + }, + + + /*** + * Creates an ApiCaller instance for the record type and the given options. + * @param recordType + * @param parameters: The options are usually based on the Sproutcore query parameters, but the api call + * @param method: Optional: 'PATCH', 'GET', etc + * is only interested in certain parameters, which sometimes depend on the recordType + */ + recordTypeToApiCaller: function(recordType, parameters, method) { + parameters = parameters || {}; + // Map the recordType API name to another name, if toApiRecordType doesn't already do the job + var apiModelName = this.toApiResourceName(recordType.apiRecordType(parameters, method)); + //var options = {csrf_token:'{{ csrf_token }}'}; + var uriPath, + uriOptions, + // The id to append to the url for single record queries + id = parameters && parameters.id && parameters['id'], + ids = parameters && parameters.ids && parameters['ids']; + + // All other Tastypie calls + uriPath = this._constructUri(apiModelName, {id:id, ids:ids}); + uriOptions = $.extend( + {}, + {format:'json', limit:10000}, + Footprint.userController.get('status') & SC.Record.READY ? { + api_key:Footprint.userController.getPath('content.firstObject.api_key'), + username:Footprint.userController.getPath('content.firstObject.username') } + : {}, + // Add the options. + this._contextParameters(recordType, parameters, method) + ); + + return this.createCallerForUri(uriPath, uriOptions, recordType); + }, + + /*** + * Adds contextual parameters to resolve dynamic subclasses on the server + * @param recordType + * @param parameters + * @private + */ + _contextParameters: function(recordType, parameters, method) { + var modified_parameters = {}; + if (parameters.layer) { + // Convert any reference to a Footprint.Layer to a layer__id parameter + modified_parameters['layer__id'] = parameters.layer.get('id'); + } + else if (method=='PATCH' && [Footprint.Feature, Footprint.LayerSelection].contains(recordType.apiRecordType())) { + // Hacky, but we the API needs at the moment to resolve the LayerSelection class + modified_parameters['layer__id'] = Footprint.layerSelectionActiveController.getPath('layer.id'); + } + else if (['PATCH', 'POST', 'PUT'].contains(method) && [Footprint.Scenario].contains(recordType.apiRecordType())) { + // DITTO + modified_parameters['origin_config_entity__id'] = + Footprint.scenariosEditController.getPath('selection.firstObject.origin_config_entity.id'); + } + + if (parameters.config_entity || parameters.parent_config_entity) { + // Many recordTypes, including Feature subclasses, belong to a ConfigEntity instance. Pass it to the API + // to filter the results. + $.extend( + modified_parameters, + parameters.config_entity ? + {config_entity__id:parameters.config_entity.get('id')} : + {parent_config_entity__id:parameters.parent_config_entity.get('id')} + ); + } + + if (parameters.layer_selection) { + // If a layer_selection parameter is passed, send both its id and the layer id, since layer_selection + // classes are specific to layers + modified_parameters['layer_selection__id'] = parameters.layer_selection.get('id'); + modified_parameters['layer__id'] = parameters.layer_selection.getPath('layer.id'); + } + + return Object.keys(modified_parameters).length > 0 ? + modified_parameters : + parameters; + }, + + // TODO make this into something that's easy to cache + createCallerForUri: function(uriPath, uriOptions) { + + var uri = '%@?%@'.fmt( + uriPath, + $.map(uriOptions, + function(value, key) { + return '%@=%@'.fmt(key, value); + } + ).join('&')); + + return Footprint.ServerApiCaller.create({ + uri: uri + }); + } +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/data_sources/fixtures_data_sources.js b/sproutcore/apps/fp/data_sources/fixtures_data_sources.js new file mode 100644 index 000000000..eabbfebeb --- /dev/null +++ b/sproutcore/apps/fp/data_sources/fixtures_data_sources.js @@ -0,0 +1,140 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.FixturesDataSource = SC.FixturesDataSource.extend( + /** @scope CrudSample.AutoIdFixturesDataSource.prototype */ { + + /** + * Let's simulate calling a remote server for CRUD operations + */ + //simulateRemoteResponse: YES, + + /** + * Assume we have a slow server that takes 1 second to respond + */ + //latency: 5, + + /** + * The next number to allocate to a primary key + */ + nextNumber: 1000000, + + /** + * Override this method so that we can allocate ID based on a number that starts at 1,000,000. We don't start at + * 1 because that is within range of our primary key in our fixtures. We also want to return a number and not a + * string. + * + * @param recordType + * @param dataHash + * @param store + * @param storeKey + */ + generateIdFor: function(recordType, dataHash, store, storeKey) { + return this.nextNumber++; + }, + + loadFixturesFor: function(store, recordType, ret) { + sc_super(); + }, + + /** + * Override _createRecords so that we can check for unique usernames + * + * @param store + * @param storeKeys + */ + /* + _createRecords: function(store, storeKeys) { + storeKeys.forEach(function(storeKey) { + try { + var id = store.idFor(storeKey), + recordType = store.recordTypeFor(storeKey), + dataHash = store.readDataHash(storeKey), + fixtures = this.fixturesFor(recordType); + + this.validateUniqueUsername(dataHash); + + if (!id) { + id = this.generateIdFor(recordType, dataHash, store, storeKey); + } + this._invalidateCachesFor(recordType, storeKey, id); + fixtures[id] = dataHash; + store.dataSourceDidComplete(storeKey, null, id); + + } catch (e) { + // We have an error + store.dataSourceDidError(storeKey, e); + } + }, this); + }, + */ + + /** + * Override _updateRecords so that we can check for unique usernames + * @param store + * @param storeKeys + */ + /* + _updateRecords: function(store, storeKeys) { + storeKeys.forEach(function(storeKey) { + try { + var hash = store.readDataHash(storeKey); + this.validateUniqueUsername(hash); + this.setFixtureForStoreKey(store, storeKey, hash); + store.dataSourceDidComplete(storeKey); + } catch (e) { + // We have an error + store.dataSourceDidError(storeKey, e); + } + }, this); + }, + */ + + /** + * Checks if the username is unique in local store + * This simulates checking on the server side + * + * @param storeKey Store key of the user record to check + * @throws SC.Error + */ + /* + validateUniqueUsername: function(dataHash) { + var username = dataHash.username; + var query = SC.Query.local(CrudSample.UserRecord, { + conditions: '(username = {name})', + name: username + }); + var userRecords = CrudSample.store.find(query); + var count = userRecords.get('length'); + if (count == 0) { + return; + } else { + if (count == 1) { + // Check that we are not matching ourselves + var dataHashPrimaryKey = dataHash.userId; + if (dataHashPrimaryKey && dataHashPrimaryKey != undefined) { + var primaryKey = userRecords.objectAt(0).get('id'); + if (dataHashPrimaryKey != primaryKey) { + throw SC.Error.desc('Username already exists', 'username'); + } + } + } else { + // Error - more than 1 match for whatever reason + throw SC.Error.desc('Username already exists', 'username'); + } + } + } + */ +}); diff --git a/sproutcore/apps/fp/data_sources/server_api_caller.js b/sproutcore/apps/fp/data_sources/server_api_caller.js new file mode 100644 index 000000000..1371b4c1e --- /dev/null +++ b/sproutcore/apps/fp/data_sources/server_api_caller.js @@ -0,0 +1,118 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('data_sources/server_api_caller'); + +Footprint.Request = SC.Request.extend({ + patchUrl: function(address, body) { + var req = this.create().set('address', address).set('type', 'PATCH'); + if(body) { req.set('body', body) ; } + return req ; + } +}); + +Footprint.ServerApiCaller = SC.Object.extend({ + url: null, + viewModel: null, + protectedViewModel: null, + /*** + * Creates the viewModel instance according to the structure of the JSON data + */ + load: function(datasource, success, notificationData) { + Footprint.Request.getUrl(this.uri) + .set('isJSON', YES) + .notify(datasource, success, notificationData) + .send(); + }, + + create: function(datasource, success, data) { + // We always PATCH instead of POST so that we can support multiple objects + // Tastypie post_list doesn't seem to actually process more than on object + this.write(Footprint.Request.postUrl, 'PATCH', 'postUrl', datasource, success, data); + }, + + update: function(datasource, success, data, method) { + this.write(Footprint.Request.patchUrl, method || 'PATCH', 'patchUrl', datasource, success, data); + }, + + write: function(func, method, call, datasource, success, data) { + var storeKeys = data.storeKeys || [data.storeKey]; + // Look up the request function name ('postUrl', 'patchUrl', etc.) + // Footprint.Request extends SC.Request to implement patchUrl + Footprint.Request[call](this.uri).header($.extend({ + 'Accept': 'application/json'}, + YES || method=='PATCH' ? {'X-HTTP-Method-Override': method} : {} + )).json() + .notify(datasource, success, data) + .send({ + objects: storeKeys.map(function(storeKey) { + // Get the dataHash for the storeKey, performing any recordType specific preprocessing + var dataHash = this._processDataHash(data.store.readDataHash(storeKey), data.recordType, data.store.materializeRecord(storeKey)); + // Get the parent store data hash so that we only send values that have actually changed + // Don't create an originalDataHash for LayerSelection. We want to always submit all its attributes for now + var originalDataHash = data.store.parentStore && ![Footprint.LayerSelection].contains(data.recordType) ? + this._processDataHash(data.store.parentStore.readDataHash(storeKey), data.recordType) : + null; + + return datasource._transformSproutcoreJsonToTastypie(data.store, dataHash, originalDataHash, data.recordType); + }, this) + }); + }, + _processDataHash: function(dataHash, recordType, record) { + return recordType.processDataHash(dataHash, record); + }, + + // TODO ALL BELOW + saveAsDraft: function(success) { + var saveDraftUri = '%@%@'.fmt('/draft/save', uriPath); + $.ajax( + saveDraftUri, { + type:'POST', + contentType:'application/json', + data:JSON.stringify($.merge({}, {draft:true},this.unmap())), + success:success, + error:function(jqXHR, textStatus, errorThrown) {} + } + ); + }, + + revertToCurrent: function(success) { + // TODO this could discard the draft on the server + this.update(success) + }, + recoverDraft: function(success) { + var loadDraftUri = '%@%@'.fmt('/draft/load', uriPath); + this._ajax(loadDraftUri, function(data) { + this.update(data); + success(data); + }); + }, + getRevisions: function(success) { + var loadDraftUri = '%@%@'.fmt('/draft/load', uriPath); + this._ajax(loadDraftUri, function(data) { + _update(data); + }); + }, + + revertToRevision: function(success) { + var loadDraftUri = '%@%@'.fmt('/draft/load', uriPath); + this._ajax(loadDraftUri, function(data) { + _update(data); + }); + }, + + _ajax: function(uri, success) { + $.getJSON(uri, success); + }, +}); diff --git a/sproutcore/apps/fp/footprint_statechart.js b/sproutcore/apps/fp/footprint_statechart.js new file mode 100644 index 000000000..e610a0c62 --- /dev/null +++ b/sproutcore/apps/fp/footprint_statechart.js @@ -0,0 +1,44 @@ + +/*** + * The Footprint Statechart. This extends SC.Statechart, which is a simple class that mixes in SC.StatechartManager + * See SC.StatechartManager to understand or add functionality to this class. + * @type {*} + */ +Footprint.Statechart = SC.Statechart.extend({ + trace: YES, + + rootState: SC.State.extend({ + initialSubstate: 'applicationReadyState', + + // Initial load state + applicationReadyState: SC.State.plugin('Footprint.ApplicationReadyState'), + // Login state + loggingInState: SC.State.plugin('Footprint.LoggingInState'), + // Loading state, which loads all configuration data required to show the app + loadingAppState: SC.State.plugin('Footprint.LoadingAppState'), + // Main application state, which delegates different sections of the application to substates + showingAppState: SC.State.plugin('Footprint.ShowingAppState'), + // For test + testAppState: SC.State.plugin('Footprint.TestAppState'), + /*** + * Communication to all the statechart's current states, which are handled by the first state that expects + * that event, and otherwise sent to the state's child states until a handler is found. + * We broadcasting events of a controller's status + * The event name is always in the form controllerInstanceNameIsStatusString where status string is capital camel case + */ + sendStatusEvent: function(controller, controllerName) { + this.sendEvent('%@Is%@'.fmt(controllerName, getStatusString(controller.get('status')).toLowerCase().camelize().capitalize())); + }, + + doLogout: function() { + Footprint.userController.destroyCookie(); + Footprint.mainPage.get('mainPane').remove(); + // TODO, we could keep stuff around that is user agnostic to save time + Footprint.store.reset(); + Footprint.loadingStatusController.set('content', null); + this.gotoState('loggingInState') + } + }) +}); + +Footprint.statechart = Footprint.Statechart.create(); diff --git a/sproutcore/apps/fp/footprint_theme.js b/sproutcore/apps/fp/footprint_theme.js new file mode 100644 index 000000000..7089970fc --- /dev/null +++ b/sproutcore/apps/fp/footprint_theme.js @@ -0,0 +1,33 @@ +// ========================================================================== +// Project: Footprint +// Copyright: @2012 My Company, Inc. +// ========================================================================== +/*globals Footprint */ + +// This is the theme that defines how your app renders. +// +// Your app is given its own theme so it is easier and less +// messy for you to override specific things just for your +// app. +// +// You don't have to create the whole theme on your own, though: +// your app's theme is based on SproutCore's Ace theme. +// +// NOTE: if you want to change the theme this one is based on, don't +// forget to change the :css_theme property in your buildfile. +Footprint.Theme = SC.AceTheme.create({ + name: 'footprint' +}); + +// SproutCore needs to know that your app's theme exists +SC.Theme.addTheme(Footprint.Theme); + +// Setting it as the default theme makes every pane SproutCore +// creates default to this theme unless otherwise specified. +SC.defaultTheme = 'footprint'; + +var ellipsesLabelRenderDelegate = Footprint.Theme.labelRenderDelegate.extend({ + needsEllipsis:YES +}); +Footprint.Theme.ellipsesLabelRenderDelegate = ellipsesLabelRenderDelegate; +Footprint.Theme.themes['source-list'].ellipsesLabelRenderDelegate = ellipsesLabelRenderDelegate; diff --git a/sproutcore/apps/fp/lib/index.rhtml b/sproutcore/apps/fp/lib/index.rhtml new file mode 100644 index 000000000..60554529f --- /dev/null +++ b/sproutcore/apps/fp/lib/index.rhtml @@ -0,0 +1,199 @@ +<% # SPROUTCORE DEFAULT INDEX TEMPLATE + # This template provide provides a basic wrapper for a SproutCore client. + # Most of the time, it will be sufficient for your own needs. However, if + # you need to create your own template, you can do so by copying this file + # into your client, naming it 'index.rhtml' and then adding the options + # :layout => 'lib/index' to your Buildfile. + # + # See the comments in this file for more information on what you can + # change. +-%> + + manifest="manifest.appcache"<% end %> class="no-js" lang="<%=language%>"> + + + + + + + + + + + + <% if config.touch_enabled %> + <% if config.precomposed_icon %> + + <% elsif config.icon %> + + <% end %> + <% if config.startup_image_portrait %> + + <% end %> + <% if config.startup_image_landscape %> + + <% end %> + <% end %> + + <% if config.favicon %> + + <% end %> + + +<% # Set the 'title' in your config to alter this setting %> + <%= title %> +<% # + # You may choose to load one or more bootstrap resources. These are + # JS targets that you would like to load at the top of the page. Specify + # these in your Buildfile with the 'bootstrap' config. +-%> + + + + <%= bootstrap %> + + <% # + # This line should appear in your head area to include the stylesheets + # generated by your client. If you need to include your own + # stylesheets, you don't need to change it here. Instead use the + # required option in your config. + -%> + + + + <%= @content_for_page_styles %> + + + +<% # The theme CSS class is added automatically based on your chosen theme. + # If you need to specify a custom theme name, use CONFIG.theme_name +-%> + + +<% # This section is used to setup additional optional class names on the + # body content based on JS-selected conditions. Use this to make sure you + # show the proper CSS as soon as the page appears. +-%> +<%= inline_javascript('sproutcore/bootstrap:setup_body_class_names') %> + +<% # + # This is where you root body element will appear. To cause your + # content to appear here, just declare content_for('body') in one of + # your partials. +-%> +<%= @content_for_body %> +<% # + # This is where your loading screen will appear. To add a loading screen + # just declare content_for('loading') in one of your partials. If you use + # sc-gen to generate your app, it will create a file called loading.rhtml + # under english.lproj. You can insert the desired content there. +-%> +<% unless @content_for_loading.blank? %> +
    +<%= @content_for_loading %> +
    +<% end -%> +<% # + # This is where the resources you declare will appear. By default anything + # you add to partials will be added to this section unless you specify + # otherwise. Note that resources are initially hidden so you can pull them + # apart as needed on page load. +-%> +<% unless @content_for_resources.blank? %> + + +<% end -%> +<% # + # This line should appear at the bottom of your page to include your + # generated JavaScript and any libraries you reference. If you need + # to include other javascripts, add them to the :requires option of + # your client in routes.rb instead of changing it here. +-%> +<%= javascripts_for_client %> +<% unless @content_for_page_javascript.blank? %> +<%= @content_for_page_javascript %> +<% end -%> +<% # If config.html5_history is true, HTML5 history will be activated for + # SC.routes and the SC.routes.baseURI setting will be automatically + # set. +-%> +<% if config.html5_history %> +<%# TODO: Can we check for the framework in Ruby instead? %> + +<% end %> +<% # + # If you use old-style view helpers in your page, this method must be + # called to actually add the page views to your HTML. Normally this will + # not generate any content. +-%> +<% #render_page_views -%> +<% # Older SproutCore applications need SC.didLoad to be called after onload. + # This is no longer required by SproutCore so it is off by default. To + # reenable set use_window_onload = true in yur config. +-%> +<% if config.use_window_onload %> + + +<% end -%> +<% # + # The final content section can be used to add any last minute setup you + # need to do before the page ends. This is required for loading unit + # tests among other things. +-%> +<%= @content_for_final -%> +<% + #disable main if the application is loaded in design mode +%> +<%= @content_for_designer -%> + + + + diff --git a/sproutcore/apps/fp/main.js b/sproutcore/apps/fp/main.js new file mode 100644 index 000000000..f60f3a380 --- /dev/null +++ b/sproutcore/apps/fp/main.js @@ -0,0 +1,32 @@ + +// These need to load in the specified order +sc_require('resources/jquery_ui/jquery_ui_core'); +sc_require('resources/jquery_ui/widget'); +sc_require('resources/jquery_ui/colorpicker'); +sc_require('resources/jquery_ui/mouse'); +sc_require('states/test_states'); + +Footprint.main = function main() { + Footprint.statechart.initStatechart(); + // The statechart is the deafultResponder. It will delegate actions throughout the hierarchy of active states + // until a states responds to the action + SC.RootResponder.responder.set('defaultResponder', Footprint.statechart); + + Footprint.DO_STATE_TESTS = NO; + // + // Skipping the login page for now to save time + setUserContent(); + + Footprint.statechart.gotoState('applicationReadyState'); +}; +function setUserContent() { + if ((Footprint.store.dataSource.kindOf && Footprint.store.dataSource.kindOf(Footprint.DataSource)) || Footprint.store.dataSource == 'Footprint.DataSource') { + // TODO this precooked username and password should be something always created on the server for testing + var users = Footprint.store.createRecords(Footprint.User, [{id:1, username:'test', api_key:'TEST_API_KEY' + ''}]); + Footprint.userController.set('content', users); + Footprint.userController.set('userDefaults', SC.UserDefaults.create({ appDomain: "Footprint", userDomain:users.get('firstObject') })); + } +} + + +function main() { Footprint.main(); } diff --git a/sproutcore/apps/fp/models/backup_properties_mixin.js b/sproutcore/apps/fp/models/backup_properties_mixin.js new file mode 100644 index 000000000..9fc6baf9c --- /dev/null +++ b/sproutcore/apps/fp/models/backup_properties_mixin.js @@ -0,0 +1,39 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.BackupProperties = { + /** + * Return an object containing a backup of the properties + * @returns SC.Object object containing properties to backup + */ + backupProperties: function() { + var backup = SC.Object.create(); + for (var i = 0; i < this.properties.length; i++) { + var p = this.properties[i]; + backup.set(p, this.get(p)); + } + return backup; + }, + + /** + * Restores properties from a backup crated by backupProperties(). + */ + restoreProperties: function(backup) { + for (var i = 0; i < this.properties.length; i++) { + var p = this.properties[i]; + this.set(p, backup.get(p)); + } + return; + } +} diff --git a/sproutcore/apps/fp/models/built_form_models.js b/sproutcore/apps/fp/models/built_form_models.js new file mode 100644 index 000000000..cbeb58b9f --- /dev/null +++ b/sproutcore/apps/fp/models/built_form_models.js @@ -0,0 +1,189 @@ +sc_require('models/key_mixin'); +sc_require('models/name_mixin'); +sc_require('models/tags_mixin'); +sc_require('models/medium_models'); + +Footprint.BuildingAttributeSet = Footprint.Record.extend({ + flat_building_densities: SC.Record.toOne("Footprint.FlatBuiltForm", {nested: YES}), + building_uses: SC.Record.toMany('Footprint.BuildingUsePercent', {nested: YES}), + parking_spaces: SC.Record.attr(Number), + parking_structure_square_feet: SC.Record.attr(Number), + floors: SC.Record.attr(Number), + total_far: SC.Record.attr(Number), + gross_population_density: SC.Record.attr(Number), + household_density: SC.Record.attr(Number), + impervious_roof_percent: SC.Record.attr(Number), + impervious_hardscape_percent: SC.Record.attr(Number), + pervious_hardscape_percent: SC.Record.attr(Number), + softscape_and_landscape_percent: SC.Record.attr(Number), + irrigated_percent: SC.Record.attr(Number), + hardscape_percent: SC.Record.attr(Number), + residential_irrigated_square_feet: SC.Record.attr(Number), + commercial_irrigated_square_feet: SC.Record.attr(Number), + residential_average_lot_size: SC.Record.attr(Number), + gross_net_ratio: SC.Record.attr(Number), + intersection_density: SC.Record.attr(Number), + combined_pop_emp_density: SC.Record.attr(Number) +}); + +Footprint.BuildingUseDefinition = Footprint.Record.extend({ + name: SC.Record.attr(String) +}); + +Footprint.BuildingUsePercent = Footprint.Record.extend({ + building_use_definition: SC.Record.toOne("Footprint.BuildingUseDefinition", {nested: YES}), + percent: SC.Record.attr(Number), + vacancy_rate: SC.Record.attr(Number), + household_size: SC.Record.attr(Number), + efficiency: SC.Record.attr(Number), + square_feet_per_unit: SC.Record.attr(Number), + floor_area_ratio: SC.Record.attr(Number), + unit_density: SC.Record.attr(Number), + gross_built_up_area: SC.Record.attr(Number), + net_built_up_area: SC.Record.attr(Number) +}); + + +Footprint.BuiltForm = Footprint.Record.extend( + Footprint.Name, + Footprint.Tags, { + + medium: SC.Record.toOne("Footprint.Medium", {}), + media: SC.Record.toMany("Footprint.Medium", {}), + building_attribute_set: SC.Record.toOne("Footprint.BuildingAttributeSet", {isMaster: YES}), + // The examples used by the visualizer + examples: SC.Record.toMany("Footprint.BuiltFormExample"), + + // Save all of these records after the main record + _saveBeforeProperties: function() { + return ['medium', 'building_attribute_set'] //, 'examples', 'media'] + }, + + _cloneProperties: function () { + return ['medium', 'building_attribute_set'] //, 'examples', 'media']; + }, + + _copyProperties: function () { + return ['tags'] + }, + + _skipProperties: function() { + return ['origin_instance']; + }, + + // Set the origin Built Form + _cloneSetup: function(sourceRecord) { + this.set('origin_instance', sourceRecord); + }, + + _mapAttributes: { + key: function (record, key, random) { + return record.get('origin_instance') ? + 'new_%@_%@'.fmt(key, random) : + 'new_%@'.fmt(random); + }, + name: function (record, name, random) { + return record.get('origin_instance') ? + 'New %@ %@'.fmt(name, random) : + 'New %@'.fmt(random); + } + }, + +}); + +Footprint.BuiltFormSet = Footprint.Record.extend( + Footprint.Key, + Footprint.Name, { + + built_forms: SC.Record.toMany("Footprint.BuiltForm", { + nested: NO, + inverse: "built_form_set", + isMaster: YES + }), + + _copyProperties: function () { + return 'built_forms'.w(); + }, + _mapAttributes: { + key: function (record, key, random) { + return record.get('origin_instance') ? + key + 'New' : + 'New %@'.fmt(random); + }, + name: function (record, name) { + return record.get('origin_instance') ? + name + 'New' : + 'New %@'.fmt(random); + } + }, + + treeItemIsExpanded: YES, + treeItemChildren: function () { + return this.get("built_forms"); + }.property() + }); + +Footprint.FlatBuiltForm = Footprint.Record.extend({ + key: SC.Record.attr(Number), + intersection_density: SC.Record.attr(Number), + built_form_type: SC.Record.attr(String), + gross_net_ratio: SC.Record.attr(Number), + dwelling_unit_density: SC.Record.attr(Number), + household_density: SC.Record.attr(Number), + population_density: SC.Record.attr(Number), + employment_density: SC.Record.attr(Number), + single_family_large_lot_density: SC.Record.attr(Number), + single_family_small_lot_density: SC.Record.attr(Number), + attached_single_family_density: SC.Record.attr(Number), + multifamily_2_to_4_density: SC.Record.attr(Number), + multifamily_5_plus_density: SC.Record.attr(Number), + armed_forces_density: SC.Record.attr(Number), + office_density: SC.Record.attr(Number), + retail_density: SC.Record.attr(Number), + industrial_density: SC.Record.attr(Number), + residential_density: SC.Record.attr(Number), + agricultural_density: SC.Record.attr(Number) +}); + + +Footprint.PrimaryComponent = Footprint.BuiltForm.extend({ + primary_component_percent_set: SC.Record.toMany('Footprint.PrimaryComponentPercent', {isMaster: NO}), + _skipProperties: function () { + return (sc_super() || []).concat(['primary_component_percent_set']); + } +}); + +Footprint.PrimaryComponentPercent = Footprint.Record.extend({ + primary_component: SC.Record.toOne("Footprint.PrimaryComponent", {nested: NO, inverse:'primary_component_percent_set'}), + percent: SC.Record.attr(Number) +}); + +Footprint.PlacetypeComponent = Footprint.BuiltForm.extend({ + primary_component_percents: SC.Record.toMany('Footprint.PrimaryComponentPercent', {nested: YES}), + placetype_component_percent_set: SC.Record.toMany('Footprint.PlacetypeComponentPercent', {isMaster: NO}), + _copyProperties: function () { + return (sc_super() || []).concat(['primary_component_percents']); + }, + _skipProperties: function () { + return (sc_super() || []).concat(['placetype_component_percent_set']); + } +}); + +Footprint.PlacetypeComponentPercent = Footprint.Record.extend({ + placetype_component: SC.Record.toOne("Footprint.PlacetypeComponent", {nested: NO, inverse:'placetype_component_percent_set'}), + percent: SC.Record.attr(Number) +}); + + +Footprint.Placetype = Footprint.BuiltForm.extend({ + placetype_component_percents: SC.Record.toMany('Footprint.PlacetypeComponentPercent', {nested: YES}), + _copyProperties: function () { + return (sc_super() || []).concat(['placetype_component_percents']); + } +}); + +Footprint.BuiltFormExample = Footprint.Record.extend({ + url_aerial: SC.Record.attr(String), + url_street: SC.Record.attr(String), + content: SC.Record.attr(Object) +}); diff --git a/sproutcore/apps/fp/models/categories_mixin.js b/sproutcore/apps/fp/models/categories_mixin.js new file mode 100644 index 000000000..a4c27c71d --- /dev/null +++ b/sproutcore/apps/fp/models/categories_mixin.js @@ -0,0 +1,23 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/category_model'); + +Footprint.Categories = { + categories: SC.Record.toMany("Footprint.Category", { + nested:true, + isMaster:YES + }) +} + diff --git a/sproutcore/apps/fp/models/category_model.js b/sproutcore/apps/fp/models/category_model.js new file mode 100644 index 000000000..11f28d4bf --- /dev/null +++ b/sproutcore/apps/fp/models/category_model.js @@ -0,0 +1,18 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.Category = Footprint.ChildRecord.extend({ + key:SC.Record.attr(String), + value:SC.Record.attr(String) +}); diff --git a/sproutcore/apps/fp/models/config_entity_models.js b/sproutcore/apps/fp/models/config_entity_models.js new file mode 100644 index 000000000..95435820d --- /dev/null +++ b/sproutcore/apps/fp/models/config_entity_models.js @@ -0,0 +1,203 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/config_entity_selections_mixin'); +sc_require('models/geographic_bounds_mixin'); +sc_require('models/name_mixin'); +sc_require('models/key_mixin'); +sc_require('models/categories_mixin'); +sc_require('models/backup_properties_mixin'); + +/*** + * Creates a special equality operator $ to evaluate if two records have the same id to get around the query + * language's uncanny inability to handle inheritance + */ +SC.Query.registerQueryExtension( + '$', { + reservedWord: true, + leftType: 'PRIMITIVE', + rightType: 'PRIMITIVE', + evalType: 'BOOLEAN', + + /** @ignore */ + evaluate: function (r, w) { + var left = this.leftSide.evaluate(r, w); + var right = this.rightSide.evaluate(r, w); + return left && right && left.get('id') == right.get('id') + }}); + +Footprint.ConfigEntity = Footprint.Record.extend( + Footprint.ConfigEntitySelections, + Footprint.GeographicBounds, + Footprint.Name, + Footprint.Key, + Footprint.Categories, + Footprint.BackupProperties, { + + isPolymorphic: YES, + deleted: SC.Record.attr(Boolean), + parent_config_entity: SC.Record.toOne('Footprint.ConfigEntity', { isMaster: YES}), + origin_config_entity: SC.Record.toOne('Footprint.ConfigEntity', { isMaster: YES}), + media: SC.Record.toMany('Footprint.Medium', { nested: NO}), + + presentations: SC.Record.toOne("Footprint.PresentationTypes", { + nested:YES + }), + + policy_sets: SC.Record.toMany("Footprint.PolicySet"), + + built_form_sets: SC.Record.toMany("Footprint.BuiltFormSet"), + + // The API returns the DbEntities within DbEntityInterests + db_entity_interests : SC.Record.toMany("Footprint.DbEntityInterest"), + + /*** + * Maps the db_entity_interests to retrieve the underlying db_entities + */ + db_entities: function() { + return this.get('db_entity_interests').mapProperty('db_entity'); + }.property('db_entity_interests').cacheable(), + + db_entity_by_key: function(key) { + return this.get('db_entities').grep(function(db_entity) { + return db_entity.get('key') == key; + })[0]; + }, + + // When cloning, reference these properties--don't clone them + _copyProperties: function () { + return ['parent_config_entity']; + }, + // When cloning, clone these properties, creating new records for each + _cloneProperties: function () { + // This is done on the server for now + return []; + }, + _skipProperties: function() { + // Skip most everything from now and let the server handle it + return 'policy_sets built_form_sets categories db_entity_interests presentations selections scope schema'.w(); + }, + + // Distinguish the key and name in the cloned item + _mapAttributes: { + key: function (record, key, random) { + // Take the last segment to keep the key size down. The limit is the database schema + return 'new_%@_%@'.fmt(key.split('_').slice(-1)[0], random).slice(0,50); + }, + name: function (record, name, random) { + return 'New %@ %@'.fmt(name, random); + } + }, + + // Even a newly created ConfigEntity needs a parent + _createSetup: function(sourceRecord) { + sc_super() + this.set('parent_config_entity', sourceRecord.get('parent_config_entity')); + }, + // Set the origin config_entity + _cloneSetup: function(sourceRecord) { + this.set('origin_config_entity', sourceRecord); + }, + + _nonTransferableProperties: function () { + return ['origin_config_entity']; + }, + + _saveAfterProperties: function() { + return []; + // This is all just done on the server for simplicity for now + //return 'db_entity_interests presentations selections'.w(); + }, + + /** + * record type of the child ConfigEntities + */ + childRecordType: null, + /* + * Find the children based on childRecordType + * The $ makes it match on id in case the class types differ + */ + children: function () { + return Footprint.store.find(SC.Query.local( + SC.objectForPropertyPath(this.get('childRecordType')), { + conditions: 'parent_config_entity $ {configEntity} AND deleted=NO', + configEntity: this, + orderBy: 'id' + } + )) + }.property().cacheable(), + + // Defines an undo manager for the children records of this ConfigEntity. This allows CRUD operations on + // all of its children to be buffered for undo/redo actions. Thus a user might edit one child, create + // another, remove another, etc, and it would all be in a single undo buffer. This also allows bulk operations + // to be in the buffer, such as changing the Category values of several children at once. + childrenUndoManager:null, + + // Undo manager for the individual instance, + undoManager: null, + + writeStatus: function() { + sc_super() + }, + statusDidChange: function() { + if (this.get('status') === SC.Record.READY_DIRTY) { + logWarning("Scenario %@ became DIRTY!".fmt(this)); + } + }.observes('.status') +}); + +Footprint.ConfigEntity.mixin({ + // Strip out stuff that shouldn't be saved to the server + processDataHash: function(dataHash, record) { + dataHash = $.extend({}, dataHash); + if (dataHash.analysis_modules) + delete dataHash.analysis_modules; + return dataHash; + } +}); + +Footprint.GlobalConfig = Footprint.ConfigEntity.extend({ + childRecordType: 'Footprint.Region' +}); + +Footprint.Region = Footprint.ConfigEntity.extend({ + childRecordType: 'Footprint.Project', + + // The Region's parent_config_entity may be the GlobalConfig singleton or the another Region. + // Use a computed relationship to determine the record type. + // TODO this doesn't make sense + parent_config_entity: SC.Record.toOne(function () { + return this.readAttribute('parent_config_entity') == + Footprint.store.find(SC.Query.local(Footprint.GlobalConfig)).toArray()[0].get('id') ? + Footprint.GlobalConfig : Footprint.Region; + }) +}); + +Footprint.Project = Footprint.ConfigEntity.extend({ + init: function() { + sc_super(); + // Since Projects will usually make use of the childrenUndoManager, create it on init. + if (!this.get('childrenUndoManager')) + this.childrenUndoManager = SC.UndoManager.create(); + }, + + childRecordType: 'Footprint.Scenario', + base_year: SC.Record.attr(Number), + // Override ConfigEntity's definition so that the API knows to look up a Region + parent_config_entity: SC.Record.toOne('Footprint.Region', { isMaster: YES }), + // The parent_config_entity is always a Region, so we can provide this synonym property + region: function () { + return this.get('parent_config_entity'); + }.property('parent_config_entity') +}); diff --git a/sproutcore/apps/fp/models/config_entity_selections_mixin.js b/sproutcore/apps/fp/models/config_entity_selections_mixin.js new file mode 100644 index 000000000..5127d34ad --- /dev/null +++ b/sproutcore/apps/fp/models/config_entity_selections_mixin.js @@ -0,0 +1,147 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.ConfigEntitySelections = { + selections:SC.Record.toOne('Footprint.ConfigEntitySelection', { nested:true, isMaster: YES }), + + /** + * This only is needed for db_entity_interests right now, because they keyed + * In cases where the user changes an item's key, where the item is potentially part of a selection dictionary, updates the specified selection dictionary to make sure that every unique key is represented and that non-existent keys are removed. + * @param property: Access the base items with this.get(property) + * @param keyItemPath: Default 'key'. The path to the key for each item + * @param keyUpdate a dictionary of key changes from new to old, if available. This can reveal that 'foo' was updated to 'bar', thus allowing the selection for 'foo' to move to 'bar' + */ + updateSelections: function(property, keyItemPath, keyUpdate) { + // Get all items + var self = this; + var items = this.get(property); + keyItemPath = keyItemPath || 'key'; + var itemsByKey = $.mapToCollectionsObject( + items.toArray(), + function(item) { + return [item.getPath(keyItemPath)]; + }, + function(item) { return item;}); + var selectionObject = this.get('selections').get(property); + // Remove selections whose key has disappeared. + var clonedSelectionObject = $.extend({}, selectionObject); + $.each(selectionObject, function(key, items) { + if (!itemsByKey[key]) { + delete selectionObject[key]; + } + }); + // Make selection for any new keys, using keyUpdate if provided + $.map(itemsByKey, function(items, key) { + if (!selectionObject[key]) { + var oldKey = keyUpdate[key]; + // Assign the object from the old key if available, otherwise assign the first item having the matching key + selectionObject[key] = (oldKey && clonedSelectionObject[oldKey]) || itemsByKey[key][0] + } + }) + } +}; + +/*** + * + * Represents a dictionary (object) keyed by DbEntity key and valued by DbEntityInterest. This is used to store the DbEntityInterests that are selected for each key. The custom transform defined below takes care of transforming the incoming dictionary from the datasources to a dictionary with the same keys that Footprint.DbEntityInterest records as the values + * @type {*} + */ + +Footprint.DbEntityInterestDictionary = Footprint.Record.extend({ + _internal:YES, + /** + * Used by the Footprint.Datasource to learn the type of the dictionary items, which are all DbEntityInterests. Normally the DataSource inquires with the RecordAttribute for the type + * @param key + */ + resolveAttributeType: function(key) { + return Footprint.DbEntityInterest; + } +}); +SC.RecordAttribute.registerTransform(Footprint.DbEntityInterestDictionary, { + /** @private - convert the object into a DbEntityInterestDictionary instance with DbEntity values */ + to: function(obj, attr, recordType, parentRecord) { + var store = parentRecord.get('store'); + return $.mapObjectToObject( + // Incoming json object. This is a dictionary of DbEntityInterest ids, keyed by its DbEntity key + obj || {}, + // Map each key and id to the key and the resolved DbEntityInterest + function(key, id) { + return [key, store.find(Footprint.DbEntityInterest, id)]; + }, + // This is the output object, which we need to be a DbEntityInterestDictionary. This will contain an attribute for each mapped key, whose value is naturally the DbEntityInterest + function() { return Footprint.store.createRecord(Footprint.DbEntityInterestDictionary); } + ); + }, + + /** @private - convert an object to the raw form **/ + from: function(dbEntityDictionary) { + return $.mapObjectToObject( + // The DbEntityInterest object created in the to function above. + dbEntityDictionary || {}, + // Map each attribute name and DbEntityInterest value to a key-value pair where the value is simply the DbEntityInterest id + function(key, dbEntityInterest) { + // Filter by kind so that we don't try to map internal SC attributes + return isSCObjectOfKind(dbEntityInterest, Footprint.DbEntityInterest) ? + [key, dbEntityInterest.get('id')]: + null; + } + ); + }, + /*** + * Override Footprint.Record to copy the keys without cloning the values. Make the values null and set later. + * @param record + */ + copyAttributes: function(record) { + $.each(this, function(key, value) { + record.set(key, null); + }); + return record; + } + + // TODO it might be possible to use this instead of updateSelections above + //observersChildren: [] +}); + +Footprint.ConfigEntitySelection = Footprint.ChildRecord.extend({ + _internal: YES, + // TODO, cloning completion trigger isn't working for this so pretend it already cloned + _status:516, + // A list of selected or default DbEntities for every unique Key + + db_entity_interests: SC.Record.toOne(Footprint.DbEntityInterestDictionary, {isMaster:YES}), + + _cloneProperties: function() { return 'db_entity_interests'.w(); }, + + /*** + * Returns YES if the given DbEntity is the one selected for its key according to the ConfigEntitySelection.db_entities dictionary + * @param dbEntity The DbEntity to test + * @return {Boolean} + */ + isSelectedDbEntityForKey: function(dbEntity) { + return this.get('db_entities')[dbEntity.get('key')]==dbEntity; + }, + + sets: SC.Record.toOne('Footprint.ConfigEntitySelectionSet', { nested:true, isMaster:YES}) +}); + +Footprint.ConfigEntitySelectionSet = Footprint.ChildRecord.extend({ + // Points to the single selected or default built_form_set + built_form_sets: SC.Record.toOne("Footprint.BuiltFormSet", { + isMaster:YES + }), + // Points to the single selected or default policy_set + policy_sets: SC.Record.toOne("Footprint.PolicySet", { + isMaster:YES + }) +}); diff --git a/sproutcore/apps/fp/models/db_entity_models.js b/sproutcore/apps/fp/models/db_entity_models.js new file mode 100644 index 000000000..f670fee10 --- /dev/null +++ b/sproutcore/apps/fp/models/db_entity_models.js @@ -0,0 +1,112 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/shared_key_mixin'); +sc_require('models/name_mixin'); +sc_require('models/tags_mixin'); + +Footprint.DbEntity = Footprint.Record.extend( + Footprint.Key, + Footprint.Name, + Footprint.Tags, +{ + deleted: SC.Record.attr(Boolean), + query: SC.Record.attr(String), + schema: SC.Record.attr(String), + table: SC.Record.attr(String), + hosts: SC.Record.attr(Array), + url: SC.Record.attr(String), + origin_instance: SC.Record.toOne("Footprint.DbEntity"), + + // Mapping of primitive attributes to other values + _mapAttributes: { + name: function(record, name, random) { + return '%@_%@'.fmt(name, random); + }, + key: function(record, key, random) { + return '%@_%@'.fmt(key, random).slice(0,50); + }, + // The server will have to assign the schema, table, and url--never copy the values from others + schema: function(schema) { + return null; + }, + table: function(table) { + return null; + }, + url: function(url) { + return null; + } + }, + _initialAttributes: { + name: function (record, random) { + return 'New %@'.fmt(random); + }, + key: function (record, random) { + return 'new_%@'.fmt(random); + } + }, + + _skipProperties: function() { + return ['origin_instance']; + }, + + _cloneSetup: function(sourceRecord) { + this.set('origin_instance', sourceRecord); + }, + + _createSetup: function(sourceRecord) { + sc_super(); + } +}); + +Footprint.DbEntityInterest = Footprint.Record.extend({ + deleted: SC.Record.attr(Boolean), + interest: SC.Record.attr(String), + db_entity: SC.Record.toOne('Footprint.DbEntity', { + nested: YES + }), + config_entity: SC.Record.toOne("Footprint.ConfigEntity", { + isMaster:YES + }), + + _cloneProperties: function() { + return ['db_entity']; + }, + + _copyProperties: function() { + return ['config_entity']; + }, + + _saveBeforeProperties: function() { + }, + + _createSetup: function(sourceRecord) { + sc_super(); + this.set('config_entity', sourceRecord.get('config_entity')); + this.set('db_entity', this.get('store').createRecord(Footprint.DbEntity, {}, Footprint.Record.generateId())); + }, + _initialAttributes: { + interest: function (record, random) { + // This is the only type of interest we use for now + return 'owner'; + } + }, + + // DbEntityInterests need to be saved after its config_entity in the case of a new Scenario, since it is an + // association between the ConfigEntity and a DbEntity, the latter will have been saved beforehand. + // This tells the EditController to save them after the config_entity is saved, then save them + // If we nested DbEntityInterests in ConfigEntity, we could simply save them at the same time as the ConfigEntity--the API supports it + _saveAfterParent:YES +}); + diff --git a/sproutcore/apps/fp/models/geographic_bounds_mixin.js b/sproutcore/apps/fp/models/geographic_bounds_mixin.js new file mode 100644 index 000000000..efa50170b --- /dev/null +++ b/sproutcore/apps/fp/models/geographic_bounds_mixin.js @@ -0,0 +1,18 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.GeographicBounds = { + bounds:SC.Record.attr(Object) +} + diff --git a/sproutcore/apps/fp/models/key_mixin.js b/sproutcore/apps/fp/models/key_mixin.js new file mode 100644 index 000000000..8cf0e0704 --- /dev/null +++ b/sproutcore/apps/fp/models/key_mixin.js @@ -0,0 +1,28 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.Key = { + key:SC.Record.attr(String), + nameObserver: function(key, value) { + // Keys are bound to a slugified name when new + // For now only update the key if the record is new. + // Updating the key on existing records is problematic, since it's a sort of id. Although this should work someday + if (this.get('status') === SC.Record.READY_NEW) { + var key = (this.get('name') || '').dasherize().replace(/-/g, '_'); + this.setIfChanged('key', key.substr(0,50)); + } + }.observes('.name', '.status') +}; + + diff --git a/sproutcore/apps/fp/models/layer_models.js b/sproutcore/apps/fp/models/layer_models.js new file mode 100644 index 000000000..a680bd0cf --- /dev/null +++ b/sproutcore/apps/fp/models/layer_models.js @@ -0,0 +1,53 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* * You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +sc_require('models/presentation_medium_model'); + +// LayerLibrary is configuration of various Layers +Footprint.LayerLibrary = Footprint.Presentation.extend({ + // We currently use the results attribute instead of presentation_media. + // In theory Sproutcore supports subclassing on nested records, + // so we should be able to use the presentation_media attribute instead of results. + // A Result is a subclass of PresentationMedium + layers: SC.Record.toMany("Footprint.Layer", { + isMaster:YES + }), + + _copyProperties: function () { + return sc_super().concat([]); + }, + _cloneProperties: function () { + return sc_super().concat(['layers']); + } +}); + +Footprint.Layer = Footprint.PresentationMedium.extend({ + // Override superclass property to specify correct related model. + presentation: SC.Record.toOne("Footprint.LayerLibrary", { + isMaster: NO + }), + + origin_instance: SC.Record.toOne("Footprint.Layer"), + // A simple flag to indicate that a cloned layer's db_entity features should be created based on the + // current layer_selection of the origin_instance + create_from_selection: SC.Record.attr(Boolean), + + _skipProperties: function() { + return ['origin_instance']; + }, + + _cloneSetup: function(sourceRecord) { + this.set('origin_instance', sourceRecord); + } +}); diff --git a/sproutcore/apps/fp/models/layer_selection_model.js b/sproutcore/apps/fp/models/layer_selection_model.js new file mode 100644 index 000000000..316bbb676 --- /dev/null +++ b/sproutcore/apps/fp/models/layer_selection_model.js @@ -0,0 +1,109 @@ +/** + * Created by calthorpe on 12/27/13. + */ + +/*** + * Represents the sub selection of a Footprint.Layer (which is currently modeled as Footprint.PresentationMedium) + * instance's Feature instances. + * + * @type {*} + */ +Footprint.LayerSelection = Footprint.Record.extend({ + + // The unique id for the record is its combination of user id and layer id + primaryKey: 'unique_id', + + selection_layer:SC.Record.toOne("Footprint.Layer", { + isMaster:YES + }), + user:SC.Record.toOne("Footprint.User", { + isMaster:YES + }), + layer:null, + layerBinding:SC.Binding.oneWay('.selection_layer'), + + // These are generic features. We only use them in conjunction with the layer to resolve the full Feature. + features: SC.Record.attr(Array), + // The result fields of the query + result_fields: SC.Record.attr(Array), + // The pretty version of those fields to display as column titles + // TODO how do I model these? + //result_field_title_lookup: SC.Record.attr(Object), + + // The summary results, and array of dicts that the API converts to SC.Objects on load + summary_results: SC.Record.attr(Array), + // The summary fields of the summary query + summary_fields: SC.Record.attr(Array), + // The pretty version of those fields to display as column titles + // TODO how do I model these? + //summary_field_title_lookup: SC.Record.attr(Object), + + // Bounds are set to a geojson geometry to update the selection + bounds:SC.Record.attr(Object), + boundsAsString: function() { + // This does lat,lon | lat,lon rounded to four digits. + // Assumes a single polygon in multi-polygons, hence the firstObject.firstObject + lonLats = this.getPath('bounds.coordinates.firstObject.firstObject'); + return lonLats ? lonLats.map( + function(lonLat) { + return lonLat.map(function(l) {return SC.Math.round(l,4)}).join(','); + }).join('|') : null; + }.property('bounds').cacheable(), + + // A dictionary of the raw query strings + // This includes 'filter_string', 'aggregates_string', and 'group_by_string' + query_strings: SC.Record.attr(Object), + + // Holds the parsed filter token tree + filter:SC.Record.attr(Object), + // Holds the list of join db_entity_keys + joins:SC.Record.attr(Array), + // Holds the list of aggregate token trees + aggregates:SC.Record.attr(Object), + // Holds the list of group by terms as parsed token tress + group_bys:SC.Record.attr(Object), + + // The extent of the currently selection features + selection_extent:SC.Record.attr(Object), + + // Defines an undo manager for the Feature records of the label. This allows a separate undoManager per layer + featureUndoManager:null, + // Defines an undo manager for this instance + undoManager: null, + + destroy:function() { + sc_super(); + if (this.get('featureUndoManager')) + this.get('featureUndoManager').destroy(); + }, + + /*** + * Restore the user generated attributes + * @param attributes: object of raw attributes + */ + restore: function(attributes) { + ['query_strings', 'joins'].map(function(attr) { + this.set(attr, attributes[attr]); + }, this); + } +}); + +Footprint.LayerSelection.mixin({ + processDataHash: function(dataHash, record) { + // Strip out the the features. We never want to send these. + dataHash = $.extend({}, dataHash); + delete dataHash.features; + delete dataHash.summary_results; + delete dataHash.summary_fields; + delete dataHash.summary_field_title_lookup; + delete dataHash.query_sql; + delete dataHash.summary_query_sql; + + dataHash.query_strings = {} + dataHash.query_strings.filter_string = record.getPath("query_strings.filter_string"); + dataHash.query_strings.aggregates_string = record.getPath("query_strings.aggregates_string"); + dataHash.query_strings.group_by_string = record.getPath("query_strings.group_by_string"); + return dataHash; + } +}); + diff --git a/sproutcore/apps/fp/models/medium_models.js b/sproutcore/apps/fp/models/medium_models.js new file mode 100644 index 000000000..4cea34dde --- /dev/null +++ b/sproutcore/apps/fp/models/medium_models.js @@ -0,0 +1,33 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/key_mixin'); +sc_require('models/name_mixin'); + +Footprint.Medium = Footprint.Record.extend(Footprint.Key, Footprint.Name, { + name: SC.Record.attr(String), + description: SC.Record.attr(String), + key: SC.Record.attr(String), + url: SC.Record.attr(String), + content_type: SC.Record.attr(String), + content: SC.Record.attr(Object), + /** + * Since keys need to be unique when cloning, we generate unique key + */ + _mapAttributes: { + key:function(record, key, random) { + return '%@__%@'.fmt(key, random); + } + } +}); diff --git a/sproutcore/apps/fp/models/name_mixin.js b/sproutcore/apps/fp/models/name_mixin.js new file mode 100644 index 000000000..84c3bd28a --- /dev/null +++ b/sproutcore/apps/fp/models/name_mixin.js @@ -0,0 +1,19 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.Name = { + name:SC.Record.attr(String), + description:SC.Record.attr(String) +} + diff --git a/sproutcore/apps/fp/models/policy_models.js b/sproutcore/apps/fp/models/policy_models.js new file mode 100644 index 000000000..17a505f84 --- /dev/null +++ b/sproutcore/apps/fp/models/policy_models.js @@ -0,0 +1,59 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/key_mixin'); +sc_require('models/name_mixin'); +sc_require('models/tags_mixin'); + +/** + * A Mixin to identify the child items of each Policy, PolicySet or PolicyCategory for tree display + * @type {Object} + */ +Footprint.PolicyTreeItemChildren = { + treeItemIsExpanded: YES, + treeItemChildren: function(){ + return this.get("policies"); + }.property() +}; + +Footprint.Policy = Footprint.Record.extend( + Footprint.Key, + Footprint.Name, + Footprint.Tags, + Footprint.PolicyTreeItemChildren, { + + value:SC.Record.attr(Number), + // Sub policies + policies: SC.Record.toMany("Footprint.Policy", { + nested: true, + inverse: "policies", + isMaster:YES + }), + _copyProperties: function() { return 'policies'.w(); }, +}); + + + +Footprint.PolicySet = Footprint.Record.extend( + Footprint.Key, + Footprint.Name, + Footprint.PolicyTreeItemChildren, { + + policies: SC.Record.toMany("Footprint.Policy", { + nested: true, + inverse: "policy_set", + isMaster:YES + }), + _copyProperties: function() { return 'policies'.w(); }, +}); diff --git a/sproutcore/apps/fp/models/presentation_medium_model.js b/sproutcore/apps/fp/models/presentation_medium_model.js new file mode 100644 index 000000000..e2deebb87 --- /dev/null +++ b/sproutcore/apps/fp/models/presentation_medium_model.js @@ -0,0 +1,163 @@ +/** + * + * Created by calthorpe on 12/27/13. + */ + +Footprint.PresentationMedium = Footprint.Record.extend({ + isPolymorphic: YES, + deleted: SC.Record.attr(Boolean), + presentation: SC.Record.toOne("Footprint.Presentation", { + isMaster: NO + }), + tags: SC.Record.toMany("Footprint.Tag", { + nested: YES + }), + medium: SC.Record.toOne("Footprint.Medium", { + nested: YES + }), + // Layers and Results are the primary users of DbEntityInterests. Hence they nest them and can edit them. + db_entity_interest: SC.Record.toOne("Footprint.DbEntityInterest", { + nested: YES + }), + // This is what is actually owned by the PresentationMedium + // DbEntityInterest is a property of presentation.config_entity but needs to be modeled + // here so that it can be created and saved beforehand when new presentation_media are created + db_entity_key: SC.Record.attr(String), + medium_context: SC.Record.attr(Object), + configuration: SC.Record.attr(Object), + rendered_medium: SC.Record.attr(Object), + + /*** + * The resource name is formed from the db_entity_key by dasherizing and titlizing + * This works as a setter to by slugifying + */ + name: function(key, value) { + // Getter + if (value === undefined) { + var name = this.getPath('db_entity_interest.db_entity.name'); + return name; + } + // Setter + else { + // Protect us from bad state errors (hopefully). + if (this.get('status') & SC.Record.READY) { + var db_entity_key = (value || '').dasherize().replace(/-/g, '_'); + var db_entity = this.getPath('db_entity_interest.db_entity'); + // This will trigger a change on the DbEntity.key if it's new + db_entity.setIfChanged('name', value); + // Only update the key if the record is new + if (this.get('status') === SC.Record.READY_NEW) + this.setIfChanged('db_entity_key', db_entity_key); + } + } + }.property('status', 'db_entity_key').cacheable(), + + visible: SC.Record.attr(Boolean, {defaultValue: YES}), + solo: SC.Record.attr(Boolean, {defaultValue: NO}), + applicationVisible: null, + visibleObserver: function() { + this.setIfChanged('applicationVisible', this.get('visible')) + }.observes('.visible'), + + visibility: function (propKey, value) { + if (value === undefined) { + return this.get('solo') ? Footprint.SOLO : (this.get('applicationVisible') ? Footprint.VISIBLE : Footprint.HIDDEN); + } + else { + if ([Footprint.VISIBLE, Footprint.HIDDEN].contains(value)) { + // Only change the value of the visible property if VISIBLE or HIDDEN are chosen + // This allows us to maintain the visible property value while the item is soloing + // TODO We don't update the actual visible property because we don't want to dirty the record + this.set('applicationVisible', value == Footprint.VISIBLE); + } + this.set('solo', value == Footprint.SOLO); + } + }.property('applicationVisible', 'solo').cacheable(), + + sortPriority: function () { + return this.getPath('configuration.sort_priority') || 100; + }.property('configuration'), + + _copyProperties: function () { + return ['presentation']; + }, + _cloneProperties: function () { + // medium is nested so needs clone but saves with the main record + // TODO medium is done on the server for now + return ['db_entity_interest']; //['medium']; + }, + _saveBeforeProperties: function () { + return []; + }, + _customCloneProperties: function () { + return { + /*** + * When cloning a db_entity seek out the cloned config_entity of the cloned presentationMedium + * @param clonedPresentationMedium + * @param db_entity + * @returns {*} + */ + // 'db_entity': function(clonedPresentationMedium, clonedPresentation, db_entity) { + // return clonedPresentation.get('config_entity').db_entity_by_key(this.get('db_entity_key')); + // } + }; + }, + + _mapAttributes: { + name: function (record, name, random) { + return '%@_%@'.fmt(name, random) + } + }, + _initialAttributes: { + name: function (record, random) { + return 'New %@'.fmt(random); + } + }, + + // Setup for brand new instances + // sourceRecord is the architype in the case of new instance + // we have to have a presentation and medium structure based on a peer record + _createSetup: function(sourceRecord) { + // Create the DbEntityInterest first + // We need to do this before sc_super since setting name of the Layer sets that of the DbEntityInterest.DbEntity + this.set('db_entity_interest', this.get('store').createRecord(Footprint.DbEntityInterest, {}, Footprint.Record.generateId())); + this.get('db_entity_interest')._createSetup(sourceRecord.get('db_entity_interest')); + sc_super() + + this.set('presentation', sourceRecord.get('presentation')); + + // This will be replaced by the server, but is required, so copy and null out + // TODO cloneRecord should support a param to do a structural clone without copying primitive values + this.set('medium', sourceRecord.get('medium').cloneRecord()); + this.setPath('medium.key', null); + this.setPath('medium.name', null); + this.setPath('medium.description', null); + + // TODO these will be created by the server for now. + // When we start doing a style editor these will need ot be exposed + //this.set('configuration', sourceRecord.get('configuration')); + //this.set('medium_configuration', sourceRecord.get('medium_configuration')); + this.set('db_entity_key', 'Layer_' + SC.DateTime.create().get('milliseconds')); + }, + + + _deleteSetup: function() { + sc_super() + // Set nested records to be deleted as well + this.setPath('medium.deleted', YES); + this.setPath('db_entity_interest.deleted', YES); + this.setPath('db_entity_interest.db_entity.deleted', YES); + }, + + featureRecordType: function() { + if (this.get('db_entity_key')) { + // Try to get the Feature subclass of the db_entity_key or default to Feature + return Footprint.featuresActiveController.getPath( + 'dbEntityKeyToFeatureRecordType.%@'.fmt(this.get('db_entity_key'))) || + Footprint.Feature; + } + }.property('db_entity_key').cacheable(), + + undoManager: null, + solo:null +}); diff --git a/sproutcore/apps/fp/models/presentation_models.js b/sproutcore/apps/fp/models/presentation_models.js new file mode 100644 index 000000000..16fafc64f --- /dev/null +++ b/sproutcore/apps/fp/models/presentation_models.js @@ -0,0 +1,48 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('models/config_entity_models'); +sc_require('models/shared_key_mixin'); +sc_require('models/name_mixin'); +sc_require('models/db_entity_models'); +sc_require('models/medium_models'); + +Footprint.Presentation = Footprint.Record.extend( + Footprint.Key, + Footprint.Name, { + + isPolymorphic: YES, + deleted: SC.Record.attr(Boolean), + config_entity: SC.Record.toOne("Footprint.ConfigEntity", { + isMaster: NO, + inverse: 'presentations' + }), + + _mapAttributes: { + key: function (record, key) { + return key + 'New'; + }, + name: function (record, name) { + return name + 'New'; + } + }, + solos:null, + // TODO The controller's keys property is causing a recordDidChange for some reason. + // I'm thus putting this here to prevent that. It serves no purpose here + keys: null +}); + +Footprint.Style = Footprint.Medium.extend({ + isPolymorphic: YES +}); +Footprint.Template = Footprint.Medium.extend(); +Footprint.TemplateContext = Footprint.Medium.extend(); diff --git a/sproutcore/apps/fp/models/presentations_mixin.js b/sproutcore/apps/fp/models/presentations_mixin.js new file mode 100644 index 000000000..d3478bbc5 --- /dev/null +++ b/sproutcore/apps/fp/models/presentations_mixin.js @@ -0,0 +1,34 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/presentation_models'); + +/*** + * The API delivers presentations by type so that we don't have to deal with mixed inheritance in a flat list + * TODO We should make the map presentations of type Map and abstract Presentation (both here and on the server) + * @type {{maps: (SC.ManyAttribute|SC.ChildrenAttribute), results: (SC.ManyAttribute|SC.ChildrenAttribute)}} + */ + +Footprint.PresentationTypes = Footprint.Record.extend({ + _internal:YES, + layers: SC.Record.toMany('Footprint.LayerLibrary', { + isMaster:YES, + nested: YES + }), + results: SC.Record.toMany('Footprint.ResultLibrary', { + isMaster:YES, + nested: YES + }), + _cloneProperties: function() { return 'layers results'.w(); } +}); diff --git a/sproutcore/apps/fp/models/record_extensions.js b/sproutcore/apps/fp/models/record_extensions.js new file mode 100644 index 000000000..4594768cc --- /dev/null +++ b/sproutcore/apps/fp/models/record_extensions.js @@ -0,0 +1,15 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + diff --git a/sproutcore/apps/fp/models/result_models.js b/sproutcore/apps/fp/models/result_models.js new file mode 100644 index 000000000..185e22ccd --- /dev/null +++ b/sproutcore/apps/fp/models/result_models.js @@ -0,0 +1,44 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +sc_require('models/presentation_medium_model'); + +// Result is a subclass class that supports charts and grids +Footprint.Result = Footprint.PresentationMedium.extend({ +}); + +// A grid (table) result based on a data table, view, or query +Footprint.Grid = Footprint.Result.extend(); +// A chart result based on a data table, view, or query +Footprint.Chart = Footprint.Result.extend(); +// A chart that is displayed on a map as one or more layers +Footprint.LayerChart = Footprint.Result.extend(); + +// ResultLibrary is configuration of various results +Footprint.ResultLibrary = Footprint.Presentation.extend({ + // We currently use the results attribute instead of presentation_media. In theory Sproutcore supports subclassing on nested records, so we should be able to use the presentation_media attribute instead of results. A Result is a subclass of PresentationMedium + results: SC.Record.toMany("Footprint.Result", { + nested:NO, + isMaster:YES + }), + + _copyProperties: function () { + return sc_super().concat([]); + }, + _cloneProperties: function () { + return sc_super().concat(['results']); + } +}); + diff --git a/sproutcore/apps/fp/models/scenarios_models.js b/sproutcore/apps/fp/models/scenarios_models.js new file mode 100644 index 000000000..c4a270cea --- /dev/null +++ b/sproutcore/apps/fp/models/scenarios_models.js @@ -0,0 +1,53 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/config_entity_models'); + +Footprint.Scenario = Footprint.ConfigEntity.extend({ + year: SC.Record.attr(Number), + // Override ConfigEntity's definition so that the API knows to look up a Project + parent_config_entity:SC.Record.toOne('Footprint.Project'), + // The scenario that was the clone source of this scenario, if any + origin_config_entity:SC.Record.toOne('Footprint.Scenario'), + // The parent_config_entity is always a Project, so we can provide this synonym property + project:function() { + return this.get('parent_config_entity'); + }.property('parent_config_entity'), + + // Hopefully these attributes aren't required, but the idea is that Scenarios are the leaf nodes of the ScenarioCategory tree + treeItemIsExpanded: NO, + treeItemChildren: function(){ + return null; + }.property('id').cacheable(), + + _cloneProperties: function() { + return sc_super(); + }, + + core_analytic_result: SC.Record.toOne('Footprint.AnalyticResult', { isMaster: YES, inverse:'scenario'}) + +}); + +Footprint.DwellingUnitDatum = Footprint.Record.extend({ + dwelling_unit_type: SC.Record.attr(String), + value: SC.Record.attr(Number) +}); + +Footprint.ControlTotal = Footprint.Record.extend({ + value:SC.Record.attr(Number) +}); + +// TODO these are just used by the API for now +Footprint.FutureScenario = Footprint.Scenario.extend(); +Footprint.BaseScenario = Footprint.Scenario.extend(); diff --git a/sproutcore/apps/fp/models/tag_model.js b/sproutcore/apps/fp/models/tag_model.js new file mode 100644 index 000000000..841755971 --- /dev/null +++ b/sproutcore/apps/fp/models/tag_model.js @@ -0,0 +1,17 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.Tag = Footprint.Record.extend({ + tag:SC.Record.attr(String) +}); diff --git a/sproutcore/apps/fp/models/tags_mixin.js b/sproutcore/apps/fp/models/tags_mixin.js new file mode 100644 index 000000000..8cadd651c --- /dev/null +++ b/sproutcore/apps/fp/models/tags_mixin.js @@ -0,0 +1,23 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/tag_model.js'); +/*** + * It would be nice to make this a simple list of strings, but that might require a custom + * @type {*} + */ +Footprint.Tags = { + tags: SC.Record.toMany(Footprint.Tag, {nested:true, isMaster:YES}) +}; + diff --git a/sproutcore/apps/fp/models/test_models.js b/sproutcore/apps/fp/models/test_models.js new file mode 100644 index 000000000..5d2101060 --- /dev/null +++ b/sproutcore/apps/fp/models/test_models.js @@ -0,0 +1,36 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +// Space to test modeling framework + + Footprint.Foo = Footprint.ChildRecord.extend( + { + name: SC.Record.attr('String') + }); + + Footprint.Bar = Footprint.ChildRecord.extend( + { + name: SC.Record.attr('String') + }); + + Footprint.FooBar = Footprint.Record.extend({ + name: SC.Record.attr('String'), + foo: SC.Record.toOne('Footprint.Foo', { + nested: true + }), + bar: SC.Record.toOne('Footprint.Bar', { + nested: true + }) + }); diff --git a/sproutcore/apps/fp/models/user_model.js b/sproutcore/apps/fp/models/user_model.js new file mode 100644 index 000000000..63248dbfe --- /dev/null +++ b/sproutcore/apps/fp/models/user_model.js @@ -0,0 +1,20 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.User = Footprint.Record.extend({ + username: SC.Record.attr(String), + first_name: SC.Record.attr(String), + last_name: SC.Record.attr(String), + api_key: SC.Record.attr(String) +}); diff --git a/sproutcore/apps/fp/properties/plural_property.js b/sproutcore/apps/fp/properties/plural_property.js new file mode 100644 index 000000000..587b265ff --- /dev/null +++ b/sproutcore/apps/fp/properties/plural_property.js @@ -0,0 +1,54 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +/*** + * Displays the single value if a single value is shared among propKey property value of the 'content' items + * If all resolved values are the same, the value is returned. Otherwise null is returned + * If value is passed, the property value of all items is set to value + * @type {Function} + */ +Footprint.pluralProperty = function(propKey, value) { + var content = this.get('content'); + if (!content || !(this.getPath('contentStatus') & SC.Record.READY)) + return; + + if (value !== undefined) { + // Setter + content.forEach(function(item) { + // Set all items' property to the value + item.set(propKey, value) + }) + } + // Getter + // For multiple items return null unless all have the same property value + return content.mapProperty(propKey).uniq().length==1 ? content.firstObject().get(propKey) : null; +}.property('contentStatus', 'content'); + +/*** + * Displays the range shared among the propKey property values of the content items + * Assumes the propKey is the name of the content's attribute that we are interested in display in the form attribute__range + * TODO make the content and property properties function arguments + * @type {Function} + */ +Footprint.pluralRangeProperty = function(propKey, value) { + var property = propKey.split('__')[0]; + var content = this.get('content'); + if (!content || !(this.get('contentStatus') & SC.Record.READY)) + return; + + // Getter + // For multiple items return the range unless only 1 value exists + return content.mapProperty(property).uniq().length==1 ? null : 'range %@-%@'.fmt(content.mapProperty(property).min(), content.mapProperty(property).max()); +}.property('contentStatus', 'content'); diff --git a/sproutcore/apps/fp/resources/Text/Place_Type_Additional_Attributes_Oct_31_2013.csv b/sproutcore/apps/fp/resources/Text/Place_Type_Additional_Attributes_Oct_31_2013.csv new file mode 100755 index 000000000..48705b9e1 --- /dev/null +++ b/sproutcore/apps/fp/resources/Text/Place_Type_Additional_Attributes_Oct_31_2013.csv @@ -0,0 +1 @@ +name,ptid,place_type,pt__key,intersections_sqmi,block_avg_size_acres,street_pattern,building_number_of_Floors_range,building_min_number_of_Floors,building_max_number_of_Floors,building_avg_number_of_Floors,avg_estimated_building_height_feet Urban Mixed Use,1,Urban Mixed Use,pt__urban_mixed_use,200, 2.350781 ,Walkable Grid,3 - 40,3,40,20,168 Urban Residential,2,Urban Residential,pt__urban_residential,200, 2.350781 ,Walkable Grid,2 - 40,2,40,16,159 Urban Commercial,3,Urban Commercial,pt__urban_commercial,200, 2.350781 ,Walkable Grid,3 - 40,3,40,15,81 City Mixed Use,4,City Mixed Use,pt__city_mixed_use,200, 2.350781 ,Walkable Grid,2 - 40,2,40,8,69 City Residential,5,City Residential,pt__city_residential,200, 2.350781 ,Walkable Grid,2 - 17,2,17,6,65 City Commercial,6,City Commercial,pt__city_commercial,200, 2.350781 ,Walkable Grid,1 - 40,1,40,6,46 Town Mixed Use,7,Town Mixed Use,pt__town_mixed_use,200, 2.350781 ,Walkable Grid,1 - 6,1,6,4,36 Town Residential,8,Town Residential,pt__town_residential,220, 2.350781 ,Walkable Grid,2 - 4,2,4,3,34 Town Commercial,9,Town Commercial,pt__town_commercial,200, 2.350781 ,Walkable Grid,1 - 7,1,7,3,31 Village Mixed Use,10,Village Mixed Use,pt__village_mixed_use,220, 2.350781 ,Walkable Grid,1 - 6,1,6,3,31 Village Residential,11,Village Residential,pt__village_residential,180, 2.350781 ,Walkable Grid,2 - 4,2,4,3,24 Village Commercial,12,Village Commercial,pt__village_commercial,230, 2.350781 ,Walkable Grid,1 - 6,1,6,2,27 Neighborhood Residential,13,Neighborhood Residential,pt__neighborhood_residential,180, 2.350781 ,Walkable Grid,2 - 2,2,2,2,28 Neighborhood Low,14,Neighborhood Low,pt__neighborhood_low,230, 2.350781 ,Walkable Grid,2 - 3,2,3,2,44 Office Focus,15,Office Focus,pt__office_focus,45, 11.736111 ,Auto-Oriented,1 - 5,1,5,4,29 Mixed Office and R&D,16,Mixed Office and R&D,pt__mixed_office_and_rd,45, 11.736111 ,Auto-Oriented,1 - 6,1,6,2,25 Office/Industrial,17,Office/Industrial,pt__officeindustrial,40, 14.692378 ,Auto-Oriented,1 - 5,1,5,2,18 Industrial Focus,18,Industrial Focus,pt__industrial_focus,35, 16.586318 ,Auto-Oriented,1 - 2,1,2,1,16 Low-Density Employment Park,19,Low-Density Employment Park,pt__low_density_employment_park,35, 9.699265 ,Auto-Oriented,1 - 2,1,2,1,51 High Intensity Activity Center,20,High Intensity Activity Center,pt__high_intensity_activity_center,130, 6.944444 ,Auto-Oriented,1 - 40,1,40,5,31 Mid Intensity Activity Center,21,Mid Intensity Activity Center,pt__mid_intensity_activity_center,70, 6.944444 ,Auto-Oriented,1 - 7,1,7,3,24 Low Intensity Retail-Centered N'Hood,22,Low Intensity Retail-Centered N'Hood,pt__low_intensity_retail_centered_nhood,65, 8.264463 ,Auto-Oriented,1 - 6,1,6,2,19 Retail: Strip Mall/ Big Box,23,Retail: Strip Mall/ Big Box,pt__retail_strip_mall_big_box,60, 9.699265 ,Auto-Oriented,1 - 2,1,2,1,50 Industrial/Office/Res Mixed High,24,Industrial/Office/Res Mixed High,pt__industrialofficeres_mixed_high,60, 9.699265 ,Auto-Oriented,2 - 17,2,17,5,27 Industrial/Office/Res Mixed Low,25,Industrial/Office/Res Mixed Low,pt__industrialofficeres_mixed_low,60, 9.699265 ,Auto-Oriented,1 - 3,1,3,2,40 Suburban Multifamily,26,Suburban Multifamily,pt__suburban_multifamily,90, 5.739210 ,Auto-Oriented,3 - 3,3,3,4,32 Suburban Mixed Residential,27,Suburban Mixed Residential,pt__suburban_mixed_residential,90, 5.739210 ,Auto-Oriented,1 - 3,1,3,3,28 Residential Subdivision,28,Residential Subdivision,pt__residential_subdivision,90, 5.739210 ,Auto-Oriented,1 - 3,1,3,2,29 Large Lot Residential Area,29,Large Lot Residential Area,pt__large_lot_residential_area,20, 10.000000 ,Auto-Oriented,1 - 3,1,3,2,25 Rural Residential,30,Rural Residential,pt__rural_residential,15, 19.983678 ,Auto-Oriented,2 - 2,2,2,2,25 Rural Ranchettes,31,Rural Ranchettes,pt__rural_ranchettes,10, 40.000000 ,Auto-Oriented,1 - 2,1,2,2,16 Rural Employment,32,Rural Employment,pt__rural_employment,10, 74.380165 ,Auto-Oriented,1 - 2,1,2,1,82 Campus/ University,33,Campus/ University,pt_campus_university,150, 10.000000 ,Auto-Oriented,3 - 17,3,17,8,71 Institutional,34,Institutional,pt__institutional,130, 4.985216 ,Auto-Oriented,1 - 6,1,6,7,15 Parks & Open Space,35,Parks & Open Space,pt__parks_open_space,50, 40.000000 ,Auto-Oriented,1 - 1,1,1,1,15 \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/Text/Place_Type_Additional_Attributes_Oct_31_2013.txt b/sproutcore/apps/fp/resources/Text/Place_Type_Additional_Attributes_Oct_31_2013.txt new file mode 100755 index 000000000..48705b9e1 --- /dev/null +++ b/sproutcore/apps/fp/resources/Text/Place_Type_Additional_Attributes_Oct_31_2013.txt @@ -0,0 +1 @@ +name,ptid,place_type,pt__key,intersections_sqmi,block_avg_size_acres,street_pattern,building_number_of_Floors_range,building_min_number_of_Floors,building_max_number_of_Floors,building_avg_number_of_Floors,avg_estimated_building_height_feet Urban Mixed Use,1,Urban Mixed Use,pt__urban_mixed_use,200, 2.350781 ,Walkable Grid,3 - 40,3,40,20,168 Urban Residential,2,Urban Residential,pt__urban_residential,200, 2.350781 ,Walkable Grid,2 - 40,2,40,16,159 Urban Commercial,3,Urban Commercial,pt__urban_commercial,200, 2.350781 ,Walkable Grid,3 - 40,3,40,15,81 City Mixed Use,4,City Mixed Use,pt__city_mixed_use,200, 2.350781 ,Walkable Grid,2 - 40,2,40,8,69 City Residential,5,City Residential,pt__city_residential,200, 2.350781 ,Walkable Grid,2 - 17,2,17,6,65 City Commercial,6,City Commercial,pt__city_commercial,200, 2.350781 ,Walkable Grid,1 - 40,1,40,6,46 Town Mixed Use,7,Town Mixed Use,pt__town_mixed_use,200, 2.350781 ,Walkable Grid,1 - 6,1,6,4,36 Town Residential,8,Town Residential,pt__town_residential,220, 2.350781 ,Walkable Grid,2 - 4,2,4,3,34 Town Commercial,9,Town Commercial,pt__town_commercial,200, 2.350781 ,Walkable Grid,1 - 7,1,7,3,31 Village Mixed Use,10,Village Mixed Use,pt__village_mixed_use,220, 2.350781 ,Walkable Grid,1 - 6,1,6,3,31 Village Residential,11,Village Residential,pt__village_residential,180, 2.350781 ,Walkable Grid,2 - 4,2,4,3,24 Village Commercial,12,Village Commercial,pt__village_commercial,230, 2.350781 ,Walkable Grid,1 - 6,1,6,2,27 Neighborhood Residential,13,Neighborhood Residential,pt__neighborhood_residential,180, 2.350781 ,Walkable Grid,2 - 2,2,2,2,28 Neighborhood Low,14,Neighborhood Low,pt__neighborhood_low,230, 2.350781 ,Walkable Grid,2 - 3,2,3,2,44 Office Focus,15,Office Focus,pt__office_focus,45, 11.736111 ,Auto-Oriented,1 - 5,1,5,4,29 Mixed Office and R&D,16,Mixed Office and R&D,pt__mixed_office_and_rd,45, 11.736111 ,Auto-Oriented,1 - 6,1,6,2,25 Office/Industrial,17,Office/Industrial,pt__officeindustrial,40, 14.692378 ,Auto-Oriented,1 - 5,1,5,2,18 Industrial Focus,18,Industrial Focus,pt__industrial_focus,35, 16.586318 ,Auto-Oriented,1 - 2,1,2,1,16 Low-Density Employment Park,19,Low-Density Employment Park,pt__low_density_employment_park,35, 9.699265 ,Auto-Oriented,1 - 2,1,2,1,51 High Intensity Activity Center,20,High Intensity Activity Center,pt__high_intensity_activity_center,130, 6.944444 ,Auto-Oriented,1 - 40,1,40,5,31 Mid Intensity Activity Center,21,Mid Intensity Activity Center,pt__mid_intensity_activity_center,70, 6.944444 ,Auto-Oriented,1 - 7,1,7,3,24 Low Intensity Retail-Centered N'Hood,22,Low Intensity Retail-Centered N'Hood,pt__low_intensity_retail_centered_nhood,65, 8.264463 ,Auto-Oriented,1 - 6,1,6,2,19 Retail: Strip Mall/ Big Box,23,Retail: Strip Mall/ Big Box,pt__retail_strip_mall_big_box,60, 9.699265 ,Auto-Oriented,1 - 2,1,2,1,50 Industrial/Office/Res Mixed High,24,Industrial/Office/Res Mixed High,pt__industrialofficeres_mixed_high,60, 9.699265 ,Auto-Oriented,2 - 17,2,17,5,27 Industrial/Office/Res Mixed Low,25,Industrial/Office/Res Mixed Low,pt__industrialofficeres_mixed_low,60, 9.699265 ,Auto-Oriented,1 - 3,1,3,2,40 Suburban Multifamily,26,Suburban Multifamily,pt__suburban_multifamily,90, 5.739210 ,Auto-Oriented,3 - 3,3,3,4,32 Suburban Mixed Residential,27,Suburban Mixed Residential,pt__suburban_mixed_residential,90, 5.739210 ,Auto-Oriented,1 - 3,1,3,3,28 Residential Subdivision,28,Residential Subdivision,pt__residential_subdivision,90, 5.739210 ,Auto-Oriented,1 - 3,1,3,2,29 Large Lot Residential Area,29,Large Lot Residential Area,pt__large_lot_residential_area,20, 10.000000 ,Auto-Oriented,1 - 3,1,3,2,25 Rural Residential,30,Rural Residential,pt__rural_residential,15, 19.983678 ,Auto-Oriented,2 - 2,2,2,2,25 Rural Ranchettes,31,Rural Ranchettes,pt__rural_ranchettes,10, 40.000000 ,Auto-Oriented,1 - 2,1,2,2,16 Rural Employment,32,Rural Employment,pt__rural_employment,10, 74.380165 ,Auto-Oriented,1 - 2,1,2,1,82 Campus/ University,33,Campus/ University,pt_campus_university,150, 10.000000 ,Auto-Oriented,3 - 17,3,17,8,71 Institutional,34,Institutional,pt__institutional,130, 4.985216 ,Auto-Oriented,1 - 6,1,6,7,15 Parks & Open Space,35,Parks & Open Space,pt__parks_open_space,50, 40.000000 ,Auto-Oriented,1 - 1,1,1,1,15 \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/Text/placetype_descriptions b/sproutcore/apps/fp/resources/Text/placetype_descriptions new file mode 100644 index 000000000..1429afad2 --- /dev/null +++ b/sproutcore/apps/fp/resources/Text/placetype_descriptions @@ -0,0 +1,81 @@ +Place Type Narrative Descriptions: Walkable Places +Urban +Urban Mixed Use +Urban Mixed Use districts are exemplified by a variety of intense uses and building types. Typical buildings are between 3 and 40+ stories tall, with ground-floor retail space, and offices and/or residences on the floors above. Parking is usually structured below or above ground. Workers, residents, and visitors are well served by transit, and can walk or bicycle for many of their transportation needs. +Urban Residential +The most intense residential-focused type, Urban Residential areas are typically found within or adjacent to major downtowns. They include high- and mid-rise residential towers, often surrounded by 2-3 story townhomes, with some ground-floor retail space, and parking usually structured below or above ground. Residents are well served by transit, and can walk or bicycle for many of their transportation needs. +Urban Commercial +Urban Commercial areas are typically found within major Central Business Districts. They are exemplified by mid- and high-rise office towers. Typical buildings are between 20 and 40+ stories tall, with ground-floor retail space, and offices on the floors above. Parking is usually structured below or above ground; workers tend to arrive by transit, foot or bicycle in large numbers. +City +City Mixed Use +City Mixed Use areas are transit-oriented, walkable, and contain a variety of uses and building types. Typical buildings are between 3 and 30 stories tall, with ground-floor retail space, and offices and/or residences on the floors above. Parking is usually structured below or above ground. +City Residential +A very intense residential-focused type, City Residential is dominated by mid- and high-rise residential towers, with some ground-floor retail space, and often surrounded by 2-3 story townhomes. Parking is usually structured, below or above ground. Residents are well served by transit, and can walk or bicycle for many of their transportation needs. + +City Commercial +The Central Business Districts of most cities contain areas exemplary of City Commercial, with many mid- and high-rise office towers and government buildings. Typical structures are between 4 and 40 stories tall, with ground-floor retail space, and offices on the floors above. Parking is usually structured, though many workers arrive by transit, foot, or bicycle. +Town +Town Mixed Use +Town Mixed Use represents a walkable mixed-use neighborhood, such as the mixed-use core of a small city or transit oriented development, with a variety of uses and building types. Typical buildings are between 3 and 8 stories tall, with ground-floor retail space, and offices and/or residences on the floors above. Parking is usually structured, above or below ground. +Town Residential +Containing a mix of townhomes, condominiums and apartments (but generally very few, if any, single family homes), Town Residential is characterized by dense residential neighborhoods interspersed with occasional retail stores. Typical buildings are 2-5 stories tall, with limited off-street parking; residents tend to use transit, walking and bicycling for many of their transportation needs. +Town Commercial +Equivalent to the center of a traditional town, or a more employment-focused transit-oriented development, Town Commercial contains a mix of buildings set in a walkable context. Typical structures are between 2 and 8 stories tall, with ground-floor retail, and offices, services, and small amounts of residential on upper floors. +Village +Village Mixed Use +Representing the walkable mixed-use core of a traditional neighborhood, Village Mixed Use is often oriented around a transit station. Typical buildings are between 2 and 6 stories tall, with ground-floor retail space, and offices and/or residences on the floors above. Parking is structured, tucked under, or placed behind buildings so that it does not detract from the pedestrian environment. +Village Residential +Containing a mix of single-family homes on small lots and townhomes, Village Residential is characterized by traditional neighborhoods, designed to be supportive of transit service, walking and bicycling. Typical buildings are 2-3 stories tall, with small yards and an active focus on the public realm. +Village Commercial +Equivalent to the center of a small town, or a lower-intensity employment-focused transit-oriented development, Village Commercial contains a mix of buildings set in a walkable context. Typical structures are between 2 and 6 stories tall, with ground-floor retail, and offices, services, and small amounts of residential on upper floors. +Neighborhood +Neighborhood Residential +Representing a traditional neighborhood, Neighborhood Residential is characterized by single-family homes on small lots, interspersed with the occasional ground-floor retail store with a few apartments on the floors above. Typical buildings are between 2 and 3 stories tall, with small yards and an active focus on the public realm, set in a context designed to be supportive of transit service, walking and bicycling. +Neighborhood Low +Containing a mix of single-family homes on small lots interspersed with some homes on larger lots, Neighborhood Low is characterized by traditional neighborhoods, designed to be supportive of walking and bicycling. Typical buildings are 2-3 stories tall, and located within walking distance of a mixed-use neighborhood center. +Place Type Narrative Descriptions: Auto-Oriented Places +Employment Areas +Office Focus +Representing the most intense auto-oriented single-use office areas, Office Focus is characterized by office towers surrounded by parking areas. Typical buildings are between 2 and 6 stories tall. Parking can be either structured or provided on surface lots. Workers tend to arrive by auto, though densities are high enough to support suburban transit service. +Mixed Office and R&D +Representing intense suburban office/industrial/research areas, Mixed Office and R&D is characterized by a mix of employment buildings. Typical structures are 1-6 stories tall, surrounded by surface parking or the occasional parking structure. +Office/Industrial +Moderate-density suburban office and industrial areas are represented by Office/Industrial. Typical structures are 1-5 stories tall, surrounded by surface parking lots and truck loading bays. +Industrial Focus +Employment areas characterized by warehouses and industrial uses are represented by Industrial Focus. Typical structures are 1-2 stories tall, surrounded by surface parking lots and truck loading bays. +Low-Density Employment Park +Suburban low-intensity non-retail business areas are represented by the Low-Density Employment Park type; typical uses include warehousing, low-rent offices, industrial, construction yards, transportation fleet services, and freight depots. Typical structures are 1-2 stories tall, surrounded by surface parking lots and truck loading bays. +Suburban Commercial/Mixed-Use Areas +High Intensity Activity Center +Representing the most intense auto-oriented mixed-use developments, High Intensity Activity Center is generally characterized by a walkable development set in an auto-oriented context. Typical buildings are between 2 and 7 stories tall, with ground floor retail, and residential units or offices on the floors above. Parking is structured and/or provided on nearby surface lots. +Mid Intensity Activity Center +Mid Intensity Activity Center does not necessarily represent a mixed-use development type; rather, it is usually characterized by multifamily housing located immediately adjacent to a retail center, in an auto-oriented development pattern. Typical buildings are between 1 and 3 stories tall, generally surrounded by surface parking lots. +Low Intensity Retail-Centered Neighborhood +Representing the case of a block where strip commercial fronts on to an arterial, with single-family housing on the back side facing a collector street, Low Intensity Retail-Centered Neighborhood is not a mixed-use type. Set in an auto-oriented development pattern, it could be accurately described as two separate development types placed side-by-side. Typical buildings are between 1 and 2 stories tall, generally surrounded by surface parking lots. +Retail: Strip Mall / Big Box +Generally characterized by single-story retail buildings surrounded by surface parking lots, Retail: Strip Mall / Big Box represents auto-oriented retail strips and centers. While the occasional 2-3 story mall appears in this context, most buildings are single story and have a footprint that is smaller than the amount of the site devoted to parking. +Industrial/Office/Residential Mixed High +Not necessarily representing a single type of development, Industrial/Office/Residential Mixed High is rather characterized by a wide-ranging, intensely developed mix of uses located in close proximity and set in an automobile-oriented context. Building heights can range from 1 to 17 stories, and uses can include but are not limited to industrial, warehouses, offices, residential, and retail. +Industrial/Office/Residential Mixed Low +Not necessarily representing a single type of development, Industrial/Office/Residential Mixed Low is rather characterized by a wide-ranging, less-intensely developed mix of uses located in close proximity and set in an automobile-oriented context. Building heights can range from 1 to 3 stories, and uses can include but are not limited to industrial, warehouses, offices, residential, and retail. +Suburban Residential Areas +Suburban Multifamily +Predominantly containing apartments, condos, and town homes, Suburban Multifamily represents developments that may have internal walking paths but are set in an automobile-oriented context. While densities are high enough to support bus transit, residents are likely to drive for most trips. Typical buildings are 2-5 stories tall, surrounded by surface parking lots. +Suburban Mixed Residential +Suburban Mixed Residential areas contain a mix of apartments, condos, town homes, and single-family homes, with densities high enough to support bus transit. While individual developments may have internal walking paths, they are set in an automobile-oriented context, and thus residents are likely to drive for most trips. Typical buildings are 1-3 stories tall, surrounded by surface parking lots. +Residential Subdivision +Containing a mix of single-family homes on medium and large lots, Residential Subdivisions do not have densities high enough to support transit service. While individual developments may have internal walking paths, they are set in an automobile-oriented context, and thus residents are likely to drive for nearly all trips. Typical houses are 1-2 stories tall, with plentiful off-street parking. +Large Lot Residential Area +With individual single-family homes set on generously sized lots, Large Lot Residential Areas do not have densities high enough to support transit service, and tend to be located far from essential destinations. Houses are set in a decidedly automobile-oriented context, and thus residents are likely to drive for nearly all trips. Typical houses are 1-2 stories tall, with plentiful off-street parking. +Rural Residential +Homes in a Rural Residential area tend to be set on lots with average sizes of 1-2 acres, providing plentiful yard space for their owners. The rural context is decidedly automobile-oriented, and thus residents are likely to drive for nearly all trips. Typical houses are 1-2 stories tall, with plentiful off-street parking. +Rural Ranchettes +Rural Ranchettes may not be economically productive whole units, but may rather act as hobby farms for their owners, depending on a variety of factors. The rural context is decidedly automobile-oriented, and thus residents are likely to drive for nearly all trips. Typical houses are 1-2 stories tall, with plentiful off-street parking. +Rural Employment +Rural Employment areas contain a variety of land uses, including working farms, ranches, agriculturally-supportive land uses, solar installations, oil fields, and gravel pits. While the rural context is decidedly automobile-oriented, and thus residents and employees are likely to drive for nearly all trips, the low-intensity of land uses tends to keep traffic volumes low. Typical buildings are 1-2 stories tall, with plentiful off-street parking. +Campus/University +College/University areas tend to be internally walkable, though they can be located in either a walkable or auto-oriented context. Buildings can range from 1 to 20+ stories, depending on the design of the campus. Parking may be plentiful or restricted; housing may be provided on-site in large amounts, or students may commute from homes in other locations. +Institutional +Representing a variety of institutional land uses, including hospitals, prisons and other governmental facilities, Institutional areas are strongly characterized by the needs of the institution in question. +Parks & Open Space +Representing trunk open spaces or larger regional parks, Parks & Open Space areas serve a critical role in defining the greenspace of a region. These areas should be identified early on in a planning process, with their locations mapped out and protected from future development. diff --git a/sproutcore/apps/fp/resources/Text/placetype_descriptions.csv b/sproutcore/apps/fp/resources/Text/placetype_descriptions.csv new file mode 100644 index 000000000..98509a7b1 --- /dev/null +++ b/sproutcore/apps/fp/resources/Text/placetype_descriptions.csv @@ -0,0 +1,69 @@ +pt__campus_university,"College/University areas tend to be internally walkable, though they can be located in either a walkable or auto-oriented context. Buildings can range from 1 to 20+ stories, depending on the design of the campus. Parking may be plentiful or restricted; housing may be provided on-site in large amounts, or students may commute from homes in other locations." + +pt__city_commercial,"The Central Business Districts of most cities contain areas exemplary of City Commercial, with many mid- and high-rise office towers and government buildings. Typical structures are between 4 and 40 stories tall, with ground-floor retail space, and offices on the floors above. Parking is usually structured, though many workers arrive by transit, foot, or bicycle." + +pt__city_mixed_use,"City Mixed Use areas are transit-oriented, walkable, and contain a variety of uses and building types. Typical buildings are between 3 and 30 stories tall, with ground-floor retail space, and offices and/or residences on the floors above. Parking is usually structured below or above ground." + +pt__city_residential,"An intensely residential-focused type, City Residential is dominated by mid- and high-rise residential towers, with some ground-floor retail space. Parking is usually structured, below or above ground. Residents are well served by transit, and can walk or bicycle for many of their transportation needs." + +pt__high_intensity_activity_center,"High Intensity Activity Centers include a mix of moderate to intense densities of retail, office, and residential uses. They are often anchored by major regional retail centers or office parks, and while they can contain a robust mix of uses, they are most often located within an auto-oriented and non-walkable street and land use pattern. Parking can be structured and/or provided on surface lots." + +pt__industrial_focus,"Industrial Focus areas are warehouses and industrial employment areas. Typical structures are 1-2 stories tall, surrounded by surface parking lots and truck loading bays." + +pt__industrialofficeres_mixed_high,"Industrial/Office/Residential Mixed High is characterized by a wide-ranging, intensely developed mix of uses located in close proximity and set in an automobile-oriented context. Building heights can range from 1 to 15+ stories, and uses can include but are not limited to industrial, warehouses, offices, residential, and retail." + +pt__industrialofficeres_mixed_low,"Industrial/Office/Residential Mixed Low is characterized by a wide-ranging, less-intensely developed mix of uses located in close proximity and set in an automobile-oriented context. Building heights can range from 1 to 3 stories, and uses can include but are not limited to industrial, warehouses, offices, residential, and retail." + +pt__institutional,"Institutional areas include a variety of land uses, including hospitals, government facilities, prisons and other institutional uses. The design and orientation of these areas varies based on the type of use and location." + +pt__large_lot_residential_area,"Large Lot Residential Areas contain detached single-family homes set on large lots, typically within an auto-oriented street pattern. Residents are likely to drive for most trips. Typical houses are 1-2 stories tall." + +pt__low_density_employment_park,"Low-Density Employment Parks include suburban low-intensity non-retail business areas. Typical uses include warehousing, offices, industrial, construction yards, transportation fleet services, and freight depots. Typical structures are 1-2 stories tall, surrounded by surface parking lots and truck loading bays." + +pt__low_intensity_retail_centered_nhood,"Typically set in an auto-oriented development pattern, the Low Intensity Retail-Centered Neighborhood includes a commercial strip that fronts on to an arterial, with single-family or other housing types located in adjacent and surrounding areas Typical buildings are between 1 and 2 stories, generally served by surface parking." + +pt__mid_intensity_activity_center,"Mid Intensity Activity Centers include a mix of moderate densities of retail, office, and residential uses. Despite their mix of uses, they are most often oriented within an auto-oriented and non-walkable street and land use pattern. Parking is typically provided on surface lots." + +pt__mixed_office_and_rd,"Representing intense suburban office/industrial/research areas, Mixed Office and R&D is characterized by a mix of employment buildings. Typical structures are 1-6 stories tall, surrounded by surface parking and some structured parking where appropriate." + +pt__neighborhood_low,"Containing a mix of single-family homes on small lots interspersed with some medium and larger lot homes, Neighborhood Low is a traditional neighborhood area designed to support walking and bicycling. Typical buildings are 2-3 stories tall, and located within walking distance of a mixed-use neighborhood center." + +pt__neighborhood_residential,"Neighborhood Residential areas are traditional neighborhoods characterized by single-family homes on small lots, interspersed with occasional retail spaces. Typical buildings are between 2 and 3 stories tall, with small yards and an active focus on the public realm, set in a context designed to support transit service, walking, and bicycling." + +pt__office_focus,"Representing the most intensely auto-oriented single-use office areas, Office Focus is characterized by mid- and high-rise office towers. Typical buildings are between 2 and 6 stories tall. Parking can be either structured or provided on surface lots. Workers tend to arrive by auto, though densities are high enough to support suburban transit service." + +pt__officeindustrial,"Office/Industrial areas are moderate-density suburban office and industrial areas. Typical structures are 1-5 stories tall, surrounded by surface parking lots and truck loading bays." + +pt__parks_open_space,"Parks & Open Space areas include larger trunk open spaces, community and regional parks, and other large, undeveloped areas." + +pt__residential_subdivision,"Residential Subdivision areas contain a mix of mostly single-family homes on medium and large lots, typically set within an auto-oriented street pattern. Residents are likely to drive for most trips. Typical houses are 1-2 stories tall." + +pt__retail_strip_mall_big_box,"Retail: Strip Mall / Big Box areas are typically characterized by single-story retail buildings and surface parking lots. The location and design of these areas generally favors automobile access over other transport modes." + +pt__rural_employment,"Rural Employment areas contain a variety of land uses, including working farms, ranches, agriculturally-supportive land uses, solar installations, oil fields, and gravel pits. While the rural context is automobile-oriented, and thus residents and employees are likely to drive for most trips, the low-intensity of land uses tends to keep traffic volumes low. Typical buildings are 1-2 stories tall." + +pt__rural_ranchettes,"Rural Ranchettes are homes on very large lots. They could include active agricultural uses, and are typically located at the edges of urban areas. Within this rural context, residents are likely to drive for most trips. Typical houses are 1-2 stories tall." + +pt__rural_residential,"Homes in a Rural Residential area tend to be set on lots with average sizes of 1-2 acres. Within this rural context, residents are likely to drive for most trips. Typical houses are 1-2 stories tall." + +pt__suburban_mixed_residential,"Suburban Mixed Residential areas contain a mix of apartments, condos, town homes, and single-family homes, generally set within an auto-oriented street pattern. Residents are likely to drive for most trips. Typical buildings are 1-3 stories." + +pt__suburban_multifamily,"Predominantly containing apartments, condos, and town homes, Suburban Multifamily represents developments that may have internal walking paths but are set in an automobile-oriented context. While densities can be high enough to support bus transit, residents are likely to drive for most trips. Typical buildings are 2-5 stories tall, surrounded by surface parking lots." + +pt__town_commercial,"Equivalent to the center of a traditional town, or a more employment-focused transit-oriented development, Town Commercial contains a mix of buildings set in a walkable context. Typical structures are between 2 and 8 stories tall, with ground-floor retail, and offices, services, and some residential uses on upper floors." + +pt__town_mixed_use,"Town Mixed Use areas are walkable mixed-use neighborhoods, such as the mixed-use core of a small city or transit oriented development, with a variety of uses and building types. Typical buildings are between 3 and 8 stories tall, with ground-floor retail space, and offices and/or residences on the floors above. Parking is usually structured, above or below ground." + +pt__town_residential,"Containing a mix of townhomes, condominiums and apartments (but generally very few, if any, single family homes), Town Residential is characterized by dense residential neighborhoods interspersed with occasional retail areas. Typical buildings are 2-5 stories tall, with limited off-street parking. Residents tend to use transit, walking and bicycling for many of their transportation needs." + +pt__urban_commercial,"Urban Commercial areas are typically found within major Central Business Districts. They are exemplified by mid- and high-rise office towers. Typical buildings are between 20 and 40+ stories tall, with ground-floor retail space, and offices on the floors above. Parking is usually structured below or above ground; workers tend to arrive by transit, foot or bicycle in large numbers." + +pt__urban_mixed_use,"Urban Mixed Use districts are exemplified by a variety of intense uses and building types. Typical buildings are between 3 and 40+ stories tall, with ground-floor retail space, and offices and/or residences on the floors above. Parking is usually structured below or above ground. Workers, residents, and visitors are well served by transit, and can walk or bicycle for many of their transportation needs." + +pt__urban_residential,"The most intense residential-focused type, Urban Residential areas are typically found within or adjacent to major downtowns. They include high- and mid-rise residential towers, with some ground-floor retail space, and parking usually structured below or above ground. Residents are well served by transit, and can walk or bicycle for many of their transportation needs." + +pt__village_commercial,"Equivalent to the center of a small town or district, or a lower-intensity employment-focused transit-oriented development, Village Commercial contains a mix of buildings set in a walkable context. Typical structures are between 2 and 6 stories tall, with some ground-floor retail, and offices, services, and some residential on upper floors." + +pt__village_mixed_use,"Village Mixed Use areas are the walkable mixed-use cores of traditional neighborhoods. They are often oriented around a transit station. Typical buildings are between 2 and 6 stories tall, with ground-floor retail space, and offices and/or residences on the floors above. Parking is typically structured, tucked under, or placed behind buildings so that it does not detract from the pedestrian environment." + +pt__village_residential,"Containing a mix of single-family homes on small lots and townhomes, Village Residential is characterized by traditional neighborhoods, designed to be supportive of transit service, walking and bicycling. Typical buildings are 2-3 stories tall, with small yards and an active focus on the public realm." \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/Text/placetype_example_areas.csv b/sproutcore/apps/fp/resources/Text/placetype_example_areas.csv new file mode 100755 index 000000000..aad6acb61 --- /dev/null +++ b/sproutcore/apps/fp/resources/Text/placetype_example_areas.csv @@ -0,0 +1 @@ +ptid,pt__key,study_area_name,study_area_aerial_map_view,study_area_street_view 1,pt__urban_mixed_use,"Union Square, New York City, NY",http://goo.gl/maps/0nFiZ,http://goo.gl/maps/8psdm 1,pt__urban_mixed_use,"Brooklyn Heights, New York City, NY",http://goo.gl/maps/qUQyv,http://goo.gl/maps/cCQJp 1,pt__urban_mixed_use,"Brewery Blocks, Portland, OR",http://goo.gl/maps/dIKwG,http://goo.gl/maps/Jqr1G 2,pt__urban_residential,"Rincon Hill Plan, San Francisco, CA",http://goo.gl/maps/neM0b,http://goo.gl/maps/aXfes 2,pt__urban_residential,"Tenderloin, San Francisco, CA",http://goo.gl/maps/F7Yz0,http://goo.gl/maps/MhRC0 2,pt__urban_residential,"Upper West Side near 72nd Street, New York City, NY",http://goo.gl/maps/aOUec,http://goo.gl/maps/w6G18 3,pt__urban_commercial,"Financial District, San Francisco, CA",http://goo.gl/maps/beBGb,http://goo.gl/maps/MGjFd 3,pt__urban_commercial,"SOMA, San Francisco, CA",http://goo.gl/maps/95O4F,http://goo.gl/maps/I3w9U 3,pt__urban_commercial,"Downtown, Los Angeles, CA",http://goo.gl/maps/ajj8u,http://goo.gl/maps/zQmJg 4,pt__city_mixed_use,"Pearl District, Portland, OR",http://goo.gl/maps/CeXG2,http://goo.gl/maps/a7jcn 4,pt__city_mixed_use,"South Waterfront, Portland, OR",http://goo.gl/maps/GOmhQ,http://goo.gl/maps/9VjeF 4,pt__city_mixed_use,"Mission Bay, San Francisco, CA",http://goo.gl/maps/CvzEs,http://goo.gl/maps/y8x70 5,pt__city_residential,"Northwest Alphabet District, Portland, OR",http://goo.gl/maps/c8dCZ,http://goo.gl/maps/ky60p 5,pt__city_residential,"Marina District, San Diego, CA",http://goo.gl/maps/hu7Eb,http://goo.gl/maps/mUuOX 5,pt__city_residential,"Jack London Square, Oakland, CA",http://goo.gl/maps/AR62C,http://goo.gl/maps/JezjS 6,pt__city_commercial,"Downtown, Oakland, CA",http://goo.gl/maps/PYWyn,http://goo.gl/maps/YiUEG 6,pt__city_commercial,"Downtown, Sacramento, CA",http://goo.gl/maps/AAeqB,http://goo.gl/maps/iRWIW 6,pt__city_commercial,"Downtown, Portland, OR",http://goo.gl/maps/aZQte,http://goo.gl/maps/VKvCf 7,pt__town_mixed_use,"Gaslamp District (near Market & 6th streets), San Diego, CA",http://goo.gl/maps/zpYxd,http://goo.gl/maps/OLxHn 7,pt__town_mixed_use,"Downtown, Berkeley, CA",http://goo.gl/maps/sc43Y,http://goo.gl/maps/t3BqT 7,pt__town_mixed_use,"Town Center, Burbank, CA",http://goo.gl/maps/AR93Y,http://goo.gl/maps/aV6VP 8,pt__town_residential,"Mission District, San Francisco, CA",http://goo.gl/maps/NzT89,http://goo.gl/maps/JZL2t 8,pt__town_residential,"Potrero Hill, San Francisco, CA",http://goo.gl/maps/uBp9K,http://goo.gl/maps/XhEhY 8,pt__town_residential,"The Crossings, Mountain View, CA",http://goo.gl/maps/cYAJv,http://goo.gl/maps/Bxmvu 9,pt__town_commercial,"Downtown, Ashland, OR",http://goo.gl/maps/3dHO0,http://goo.gl/maps/7Ji6I 9,pt__town_commercial,"Downtown, Redwood City, CA",http://goo.gl/maps/dZO5z,http://goo.gl/maps/AA3NI 9,pt__town_commercial,"Gaslamp District (near J & Island streets), San Diego, CA",http://goo.gl/maps/5RfGm,http://goo.gl/maps/PNaHd 10,pt__village_mixed_use,"Downtown, Fairfax, CA",http://goo.gl/maps/JyH6G,http://goo.gl/maps/mfGNh 10,pt__village_mixed_use,"Orenco Station, Hillsboro, OR",http://goo.gl/maps/6Eol2,http://goo.gl/maps/jNm17 10,pt__village_mixed_use,"Fruitvale Village, Oakland, CA",http://goo.gl/maps/8VRio,http://goo.gl/maps/WQv09 11,pt__village_residential,"Richmond Transit Village, Richmond, CA",http://goo.gl/maps/EKoES,http://goo.gl/maps/Pr0ZF 11,pt__village_residential,"Park Residential, Stapleton, Denver, CO",http://goo.gl/maps/5h54O,http://goo.gl/maps/CnkHI 11,pt__village_residential,"Neighborhood near McGee Ave & Parker St, Berkeley, CA",http://goo.gl/maps/R0cEP,http://goo.gl/maps/u1U1r 12,pt__village_commercial,"Downtown, Santa Rosa, CA",http://goo.gl/maps/359HZ,http://goo.gl/maps/CphgU 12,pt__village_commercial,"Downtown, Davis, CA",http://goo.gl/maps/SQkUn,http://goo.gl/maps/bfC0A 12,pt__village_commercial,"California Ave, Palo Alto, CA",http://goo.gl/maps/VePGi,http://goo.gl/maps/mIbsb 13,pt__neighborhood_residential,"East End, Alameda, CA",http://goo.gl/maps/U80yq,http://goo.gl/maps/mO1vY 13,pt__neighborhood_residential,"Transit Oriented Neighborhood, Hercules, CA",http://goo.gl/maps/44wxq,http://goo.gl/maps/9S5If 13,pt__neighborhood_residential,"Sabin Neighborhood, Portland, OR",http://goo.gl/maps/1EaIx,http://goo.gl/maps/67Cjn 14,pt__neighborhood_low,"South Neighborhood, Stapleton, Denver, CO",http://goo.gl/maps/4ZjCu,http://goo.gl/maps/x5uVJ 14,pt__neighborhood_low,"Alameda Ridge, Portland, OR",http://goo.gl/maps/dxtrg,http://goo.gl/maps/AzZ0m 14,pt__neighborhood_low,"Eastmoreland, Portland, OR",http://goo.gl/maps/hZyCa,http://goo.gl/maps/NhK6U 15,pt__office_focus,"North 1st St, San Jose, CA",http://goo.gl/maps/jnbbg,http://goo.gl/maps/s0rB0 15,pt__office_focus,"Warner Center, Los Angeles, CA",http://goo.gl/maps/NF8FS,http://goo.gl/maps/aJdHa 15,pt__office_focus,"Howard Hughes Center, Los Angeles, CA",http://goo.gl/maps/zJ50C,http://goo.gl/maps/FL2BS 16,pt__mixed_office_and_rd,"Oyster Point, South San Francisco, CA",http://goo.gl/maps/0KzTh,http://goo.gl/maps/XX1b1 16,pt__mixed_office_and_rd,"Hacienda Business Park, Pleasanton, CA",http://goo.gl/maps/lK8SB,http://goo.gl/maps/DdVso 16,pt__mixed_office_and_rd,"Zinfandel Drive Area, Rancho Cordova, CA",http://goo.gl/maps/dMBqT,http://goo.gl/maps/WX9Sg 17,pt__officeindustrial,"Employment Park, Irvine, CA",http://goo.gl/maps/VcGV6,http://goo.gl/maps/Egbqs 17,pt__officeindustrial,"Moffett Park, Sunnyvale, CA",http://goo.gl/maps/cTuYf,http://goo.gl/maps/SbR9f 17,pt__officeindustrial,"Employment Center, Torrance, CA",http://goo.gl/maps/DU5DP,http://goo.gl/maps/DU5DP 18,pt__industrial_focus,"Intel Jones Farm Campus, Hillsboro, OR",http://goo.gl/maps/wjiaG,http://goo.gl/maps/SCrOL 18,pt__industrial_focus,"Redwood Business Park, Petaluma, CA",http://goo.gl/maps/CYYeM,http://goo.gl/maps/7dEzl 18,pt__industrial_focus,"Industrial Area, Hayward, CA",http://goo.gl/maps/H1B5Q,http://goo.gl/maps/BefI4 19,pt__low_density_employment_park,"Business Park, Cucamonga, CA",http://goo.gl/maps/ehYUV,http://goo.gl/maps/mxc1R 19,pt__low_density_employment_park,"Airport Business Area, Sacramento, CA",http://goo.gl/maps/MTOQb,http://goo.gl/maps/sYV13 19,pt__low_density_employment_park,"Business Park, Highway City, CA",http://goo.gl/maps/v0PdV,http://goo.gl/maps/Pj395 20,pt__high_intensity_activity_center,"Santana Row, San Jose, CA",http://goo.gl/maps/Kt32O,http://goo.gl/maps/PYKMm 20,pt__high_intensity_activity_center,"Bay Street, Emeryville, CA",http://goo.gl/maps/QNgij,http://goo.gl/maps/qwZUB 20,pt__high_intensity_activity_center,"Uptown, San Diego, CA",http://goo.gl/maps/HA83n,http://goo.gl/maps/y1xAk 21,pt__mid_intensity_activity_center,"Ventura Blvd & Van Nuys Blvd Area, Los Angeles, CA",http://goo.gl/maps/njol4,http://goo.gl/maps/suRQr 21,pt__mid_intensity_activity_center,"Crescent Plaza Area, Pleasant Hill, CA",http://goo.gl/maps/HDnpB,http://goo.gl/maps/e3vqL 21,pt__mid_intensity_activity_center,"Tamal Vista Blvd Area, Corte Madera, CA",http://goo.gl/maps/UxDUr,http://goo.gl/maps/EtMb0 22,pt__low_intensity_retail_centered_nhood,"Sunol Dr & Junipero St Area, Pleasanton, CA",http://goo.gl/maps/RaI24,http://goo.gl/maps/a2pU4 22,pt__low_intensity_retail_centered_nhood,"Camden Ave & Almaden Rd area, San Jose, CA",http://goo.gl/maps/ehj9y,http://goo.gl/maps/9lcPI 22,pt__low_intensity_retail_centered_nhood,"E Calaveras Blvd & N Milpitas Blvd Area, Milpitas, CA",http://goo.gl/maps/GCjHI,http://goo.gl/maps/nrpht 23,pt__retail_strip_mall_big_box,"San Antonio mini-mall, Mountain View, CA",http://goo.gl/maps/Q4NF4,http://goo.gl/maps/JXT9B 23,pt__retail_strip_mall_big_box,"Kearny Mesa, San Diego, CA",http://goo.gl/maps/YEV1m,http://goo.gl/maps/EfC61 23,pt__retail_strip_mall_big_box,"Century Blvd & Somersville Rd area, Antioch, CA",http://goo.gl/maps/kdklY,http://goo.gl/maps/EzpVS 24,pt__industrialofficeres_mixed_high,"N. Orange Grove Blvd & I-210 area, Pasadena, CA",http://goo.gl/maps/LXum0 ,http://goo.gl/maps/IGgIz 24,pt__industrialofficeres_mixed_high,"N Blackstone Ave & E Sierra Ave area, Fresno, CA",http://goo.gl/maps/BymOt ,http://goo.gl/maps/KCH8a 24,pt__industrialofficeres_mixed_high,"N Johnson Ave & W Madison Ave, El Cajon, CA",http://goo.gl/maps/w4H2u ,http://goo.gl/maps/DkGZ2 25,pt__industrialofficeres_mixed_low,"E Miner Ave & N Windsor Ave area, Stockton, CA",http://goo.gl/maps/0qSvG ,http://goo.gl/maps/KQ55I 25,pt__industrialofficeres_mixed_low,"Paxton St & Herrick Ave area, Los Angeles, CA",http://goo.gl/maps/ufw0g ,http://goo.gl/maps/bFDSz 25,pt__industrialofficeres_mixed_low,"San Jose Ave & Pomona Ave area, San Jose, CA",http://goo.gl/maps/Kixgn ,http://goo.gl/maps/4qOtP 26,pt__suburban_multifamily,"Mowry Ave & Fremont Blvd area, Fremont, CA",http://goo.gl/maps/MvqFR,http://goo.gl/maps/Ilu9S 26,pt__suburban_multifamily,"Clayton Rd & Ellis St area, Concord, CA",http://goo.gl/maps/6IS7N,http://goo.gl/maps/w7LJD 26,pt__suburban_multifamily,"S Rengstorff Ave & California St area, Mountain View, CA",http://goo.gl/maps/AZfF3,http://goo.gl/maps/OiJnH 27,pt__suburban_mixed_residential,"Plaza Vista area, Irvine, CA",http://goo.gl/maps/k3oYs,http://goo.gl/maps/qJTSl 27,pt__suburban_mixed_residential,"Ironwood area, Dublin, CA",http://goo.gl/maps/Kxpv7,http://goo.gl/maps/ASTSN 27,pt__suburban_mixed_residential,"Walnut Blvd & Sierra Dr area, Walnut Creek, CA",http://goo.gl/maps/cuzxn,http://goo.gl/maps/WHWbW 28,pt__residential_subdivision,"N Minnewawa Ave area, Fresno, CA",http://goo.gl/maps/UUO8o,http://goo.gl/maps/566A1 28,pt__residential_subdivision,"Mokelumne Dr area, Antioch, CA",http://goo.gl/maps/TRzsZ,http://goo.gl/maps/K9bOq 28,pt__residential_subdivision,"Oak Hills subdivision, Santa Clarita, CA",http://goo.gl/maps/wSSXh,http://goo.gl/maps/EORyn 29,pt__large_lot_residential_area,"Quartz Dr & Slate Dr area, Santa Rosa, CA",http://goo.gl/maps/xiQZ6,http://goo.gl/maps/CCqNn 29,pt__large_lot_residential_area,"Saddleback subdivision, Blackhawk, CA",http://goo.gl/maps/ENESh,http://goo.gl/maps/qxRNM 29,pt__large_lot_residential_area,"Douglass Ln area, Sarasota CA",http://goo.gl/maps/suyPL,http://goo.gl/maps/SnfqS 30,pt__rural_residential,"G St & 20th St, Rio Linda, CA",http://goo.gl/maps/XcS6f,http://goo.gl/maps/AZZvO 30,pt__rural_residential,"Ferguson Rd & Country Ln, Sebastopol, CA",http://goo.gl/maps/lJ6Wq,http://goo.gl/maps/qBpEi 30,pt__rural_residential,"El Nogal Ave & Rd 132, Visalia, CA",http://goo.gl/maps/qWs2V,http://goo.gl/maps/pCFNG 31,pt__rural_ranchettes,"Malcolm Dixon Rd & Green Valley Rd area, El Dorado Hills, CA",http://goo.gl/maps/tI1tB,http://goo.gl/maps/ZGSDp 31,pt__rural_ranchettes,"S Township Rd & Bogue Rd area, Yuba City, CA",http://goo.gl/maps/JuMf9,http://goo.gl/maps/Nhkb9 31,pt__rural_ranchettes,"Auburn Folsom Rd & Moss Ln area, Granite Bay, CA",http://goo.gl/maps/xnojY,http://goo.gl/maps/U2Auw 32,pt__rural_employment,"Hwy 132 & N Hart Rd area, Modesto, CA",http://goo.gl/maps/dH4tb,http://goo.gl/maps/VhGlv 32,pt__rural_employment,"S Lassen Ave & W Mt Whitney Ave area, Fresno, CA",http://goo.gl/maps/HPYCt,http://goo.gl/maps/vLuiK 32,pt__rural_employment,"Hwy 58 & Hwy 33 area, McKittrick, CA",http://goo.gl/maps/N0u8O,http://goo.gl/maps/9Y3Si 33,pt_campus_university,"Reed College, Portland, OR",http://goo.gl/maps/2rFnq,http://goo.gl/maps/SDwJn 33,pt_campus_university,"Laney College, Oakland, CA",http://goo.gl/maps/U8CPG,http://goo.gl/maps/nEfzj 33,pt_campus_university,"UC Davis, Davis, CA",http://goo.gl/maps/7mcSR,http://goo.gl/maps/xGB0f 34,pt__institutional,"Oregon Health Sciences University, Portland, OR",http://goo.gl/maps/EPjZM,http://goo.gl/maps/HIiWO 34,pt__institutional,"Antelope Valley State Prison, Lancaster, CA",http://goo.gl/maps/tPwio,http://goo.gl/maps/JQfyt 34,pt__institutional,"Palomar Medical Center, Escondido, CA",http://goo.gl/maps/0wR4t,http://goo.gl/maps/fvbTv 35,pt__parks_open_space,"Central Park, Stapleton, Denver, CO",http://goo.gl/maps/xlypg,http://goo.gl/maps/5qLKD 35,pt__parks_open_space,"Golden Gate Park, San Francisco, CA",http://goo.gl/maps/hgCHP,http://goo.gl/maps/dmvT3 35,pt__parks_open_space,"Washington Park, Portland, OR",http://goo.gl/maps/thpVa,http://goo.gl/maps/UiipZ ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, ,,,, \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/_theme.css b/sproutcore/apps/fp/resources/_theme.css new file mode 100644 index 000000000..9e20c7012 --- /dev/null +++ b/sproutcore/apps/fp/resources/_theme.css @@ -0,0 +1,19 @@ +/* + This defines the global $theme variable for use inside your CSS. + The $theme variable holds the CSS class names for your theme. + + You can then theme your app by using CSS like this: + + $theme.button { + color: blue; + @include slices('white-button.png', $left: 3, $right: 3); + } + + Any _theme.css file is prepended to all files inside its directory, + and any subdirectories that don't define their own _theme.css file. + + This allows you to give different directories different values for + $theme if you wish. +*/ +$theme: '.ace.footprint'; +@import "compass/css3"; diff --git a/sproutcore/apps/fp/resources/builtFormVis.css b/sproutcore/apps/fp/resources/builtFormVis.css new file mode 100644 index 000000000..5ae3d6b6e --- /dev/null +++ b/sproutcore/apps/fp/resources/builtFormVis.css @@ -0,0 +1,68 @@ +$theme.footprint-background-pane { + opacity: 0.7; +} +$theme.footprint-visualize-pane-content { + opacity: 1; +} + +$theme.sc-label-view.footprint-visualize-pane-header { + font-size: 24px; + padding-left: 10px; + padding-top: 10px; + text-transform: uppercase; + transition: background-color 0.5s ease; +} +$theme.footprint-visualize-pane-content { + + background-color: black; + color: white; + fill: white; +} +$theme.sectionTitle { + color: #75A1D0; + text-transform : uppercase; + font-weight : bold; +} +$theme.descriptionBlock { + border-top-right-radius: 10px; + background-color: black; + padding: 15px; +} +$theme.rightOfBarChart { + border-top-left-radius: 10px; + background-color: black; +} +$theme.devCharsTable td { + padding-left: 6px; +} +$theme.devCharsTable th { + color: #75A1D0; + text-transform: uppercase; +} +$theme.barChart { + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + transition: background-color 0.5s ease; +} +$theme.donutCharts.arc path { + stroke: #fff; + stroke-width: 0; +} +$theme.footprint-visualize-pane-footer { + transition: background-color 0.5s ease; +} +$theme.examplesList { + + background-color: #2C3035; +} +/****/ + /*the class "light" is applied to colorful regions*/ + /*on a d3 svg that are light enough (based on a calculation) to have*/ + /*black text layered on top. The default fill for text on d3 is set to*/ + /*white*/ + /***/ + +$theme.has-light-background { + color: black; + fill: black; +} diff --git a/sproutcore/apps/fp/resources/builtFormVisUtils.js b/sproutcore/apps/fp/resources/builtFormVisUtils.js new file mode 100644 index 000000000..39cd3e1eb --- /dev/null +++ b/sproutcore/apps/fp/resources/builtFormVisUtils.js @@ -0,0 +1,17 @@ +/** + * + * Created with PyCharm. + * User: calthorpe + * Date: 9/9/13 + * Time: 11:53 AM + * To change this template use File | Settings | File Templates. + */ + +function isLightColor(color) { + var rVal = color.substring(1,3); + var gVal = color.substring(3,5); + var bVal = color.substring(5,7); + + var grayscale = (parseInt(rVal,16) + parseInt(gVal,16) + parseInt(bVal,16))/3; + return (grayscale < 150) ? false : true; +} diff --git a/sproutcore/apps/fp/resources/builtform_images/default/photo1.jpg b/sproutcore/apps/fp/resources/builtform_images/default/photo1.jpg new file mode 100644 index 000000000..343df5d17 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/default/photo1.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/default/photo2.jpg b/sproutcore/apps/fp/resources/builtform_images/default/photo2.jpg new file mode 100644 index 000000000..5a9e0f1e0 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/default/photo2.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/default/photo3.jpg b/sproutcore/apps/fp/resources/builtform_images/default/photo3.jpg new file mode 100644 index 000000000..e98d78d44 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/default/photo3.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/default/photo4.jpg b/sproutcore/apps/fp/resources/builtform_images/default/photo4.jpg new file mode 100644 index 000000000..f00eb6264 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/default/photo4.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_commercial/Downtown_Sacramento_-_from_web.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_commercial/Downtown_Sacramento_-_from_web.jpg new file mode 100644 index 000000000..033faf8c6 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_commercial/Downtown_Sacramento_-_from_web.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_commercial/xxDowntown_Oakland_-_from_web.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_commercial/xxDowntown_Oakland_-_from_web.jpg new file mode 100644 index 000000000..79b9543e0 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_commercial/xxDowntown_Oakland_-_from_web.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/00_Mission_Bay_King_Street.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/00_Mission_Bay_King_Street.jpg new file mode 100644 index 000000000..7fc709538 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/00_Mission_Bay_King_Street.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/IMG_0737.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/IMG_0737.jpg new file mode 100644 index 000000000..1257b2255 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/IMG_0737.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/Mission_Bay.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/Mission_Bay.jpg new file mode 100644 index 000000000..89d90b5b3 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/Mission_Bay.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/Sc_02_CITY_Mixed_Use_10_06_08.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/Sc_02_CITY_Mixed_Use_10_06_08.jpg new file mode 100644 index 000000000..1e353a0a3 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/Sc_02_CITY_Mixed_Use_10_06_08.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/Uptown_Res.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/Uptown_Res.jpg new file mode 100644 index 000000000..d00fd8997 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_mixed_use/Uptown_Res.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/00_CIMG4368.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/00_CIMG4368.jpg new file mode 100644 index 000000000..c15b36bcb Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/00_CIMG4368.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/IMG_3316_Portland.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/IMG_3316_Portland.jpg new file mode 100644 index 000000000..cfe320a10 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/IMG_3316_Portland.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/IMG_3573.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/IMG_3573.jpg new file mode 100644 index 000000000..76bec6917 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/IMG_3573.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/IMG_5132.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/IMG_5132.jpg new file mode 100644 index 000000000..e69aaec77 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/IMG_5132.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/Portland_Hi_Den.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/Portland_Hi_Den.jpg new file mode 100644 index 000000000..b1eeb8104 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__city_residential/Portland_Hi_Den.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/00HighlandsGarden01.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/00HighlandsGarden01.jpg new file mode 100644 index 000000000..7a548771a Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/00HighlandsGarden01.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Daybreak_07-26-07_034.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Daybreak_07-26-07_034.jpg new file mode 100644 index 000000000..1cd6f7790 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Daybreak_07-26-07_034.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Daybreak_Res.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Daybreak_Res.jpg new file mode 100644 index 000000000..74de53f9a Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Daybreak_Res.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Stapleton_SF_Homes.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Stapleton_SF_Homes.jpg new file mode 100644 index 000000000..44cc47d61 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Stapleton_SF_Homes.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Stapleton_Townhomes.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Stapleton_Townhomes.jpg new file mode 100644 index 000000000..07b759ba9 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__neighborhood_residential/Stapleton_Townhomes.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/00_IMG_6322.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/00_IMG_6322.jpg new file mode 100644 index 000000000..0b61d4357 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/00_IMG_6322.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/00orenco_03.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/00orenco_03.jpg new file mode 100644 index 000000000..7e2b8c6aa Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/00orenco_03.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/Beaverton.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/Beaverton.jpg new file mode 100644 index 000000000..82b7c80d5 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/Beaverton.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/Beaverton_Mid-Density.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/Beaverton_Mid-Density.jpg new file mode 100644 index 000000000..44f81482d Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/Beaverton_Mid-Density.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/Stapleton_TC.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/Stapleton_TC.jpg new file mode 100644 index 000000000..21d80ec87 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_mixed_use/Stapleton_TC.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/00IMG_9904.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/00IMG_9904.jpg new file mode 100644 index 000000000..27e3a0772 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/00IMG_9904.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/IMG_0032.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/IMG_0032.jpg new file mode 100644 index 000000000..1dc97f52c Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/IMG_0032.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/IMG_4782.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/IMG_4782.jpg new file mode 100644 index 000000000..6f4c9b557 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/IMG_4782.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/Stapleton_Apartments.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/Stapleton_Apartments.jpg new file mode 100644 index 000000000..76ae22ee7 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/Stapleton_Apartments.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/Stapleton_Townhomes.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/Stapleton_Townhomes.jpg new file mode 100644 index 000000000..3193e5322 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__town_residential/Stapleton_Townhomes.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/10_03-31_GE_Financial_District_SF.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/10_03-31_GE_Financial_District_SF.jpg new file mode 100644 index 000000000..98ab4365c Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/10_03-31_GE_Financial_District_SF.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/Financial-District-Manhattan_from_web.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/Financial-District-Manhattan_from_web.jpg new file mode 100644 index 000000000..1abe7d92b Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/Financial-District-Manhattan_from_web.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/San_Francisco_01.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/San_Francisco_01.jpg new file mode 100644 index 000000000..b59fec7d2 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/San_Francisco_01.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/xxorh-59th-floor-view-n.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/xxorh-59th-floor-view-n.jpg new file mode 100644 index 000000000..53a8dab35 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_commercial/xxorh-59th-floor-view-n.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/00_10_03-31_GE_Downtown_Los_Angeles.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/00_10_03-31_GE_Downtown_Los_Angeles.jpg new file mode 100644 index 000000000..d54505969 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/00_10_03-31_GE_Downtown_Los_Angeles.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/10_03-31_GE_SOMA_San_Francisco.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/10_03-31_GE_SOMA_San_Francisco.jpg new file mode 100644 index 000000000..87e7402e6 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/10_03-31_GE_SOMA_San_Francisco.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/1portland_max_034.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/1portland_max_034.jpg new file mode 100644 index 000000000..8cd82f914 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/1portland_max_034.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/Brewery_blocks.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/Brewery_blocks.jpg new file mode 100644 index 000000000..55b930de9 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/Brewery_blocks.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/CIMG4405.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/CIMG4405.jpg new file mode 100644 index 000000000..16de2cedd Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/CIMG4405.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/Financial-District-City-San-Francisco-California_-_from_web.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/Financial-District-City-San-Francisco-California_-_from_web.jpg new file mode 100644 index 000000000..a31b464fc Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_mixed_use/Financial-District-City-San-Francisco-California_-_from_web.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/10_03-31_SV_Rincon_Hill_SF.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/10_03-31_SV_Rincon_Hill_SF.jpg new file mode 100644 index 000000000..7fd69881d Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/10_03-31_SV_Rincon_Hill_SF.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/Downtown-Vancouver_from_web.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/Downtown-Vancouver_from_web.jpg new file mode 100644 index 000000000..0d1d19319 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/Downtown-Vancouver_from_web.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/San_Diego_Res_Tower.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/San_Diego_Res_Tower.jpg new file mode 100644 index 000000000..a2c810c52 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/San_Diego_Res_Tower.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/Vancouver_residential_from_web_2.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/Vancouver_residential_from_web_2.jpg new file mode 100644 index 000000000..ff86b8fcb Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/Vancouver_residential_from_web_2.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/infinity_from_web.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/infinity_from_web.jpg new file mode 100644 index 000000000..1ac3ba2bd Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/infinity_from_web.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/jack_london_allegro_apts.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/jack_london_allegro_apts.jpg new file mode 100644 index 000000000..004d26ef0 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/jack_london_allegro_apts.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/xxrincon_hill_from_web.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/xxrincon_hill_from_web.jpg new file mode 100644 index 000000000..faa6d807f Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__urban_residential/xxrincon_hill_from_web.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_commercial/00San_Elijo_Center.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_commercial/00San_Elijo_Center.jpg new file mode 100644 index 000000000..61bc87239 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_commercial/00San_Elijo_Center.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_mixed_use/00San_Elijo_Village_Center.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_mixed_use/00San_Elijo_Village_Center.jpg new file mode 100644 index 000000000..b8dbafba8 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_mixed_use/00San_Elijo_Village_Center.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_mixed_use/Orenco_Main_Street.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_mixed_use/Orenco_Main_Street.jpg new file mode 100644 index 000000000..dd76a51f4 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_mixed_use/Orenco_Main_Street.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_mixed_use/San_Elijo_school_2.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_mixed_use/San_Elijo_school_2.jpg new file mode 100644 index 000000000..a1e7200cd Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_mixed_use/San_Elijo_school_2.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/00Orenco_Streetscape.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/00Orenco_Streetscape.jpg new file mode 100644 index 000000000..5b0a95535 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/00Orenco_Streetscape.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/CIMG4446.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/CIMG4446.jpg new file mode 100644 index 000000000..bcd2d80d7 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/CIMG4446.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/IMG_4796.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/IMG_4796.jpg new file mode 100644 index 000000000..f91e27dca Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/IMG_4796.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/Issaquah.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/Issaquah.jpg new file mode 100644 index 000000000..4ba2963c9 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/Issaquah.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/OrencoSF.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/OrencoSF.jpg new file mode 100644 index 000000000..78e8a8b92 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/OrencoSF.jpg differ diff --git a/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/VillageSquare_Elevation.jpg b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/VillageSquare_Elevation.jpg new file mode 100644 index 000000000..a82cfaf14 Binary files /dev/null and b/sproutcore/apps/fp/resources/builtform_images/pt__village_residential/VillageSquare_Elevation.jpg differ diff --git a/sproutcore/apps/fp/resources/d3.v3.js b/sproutcore/apps/fp/resources/d3.v3.js new file mode 100644 index 000000000..04cba441b --- /dev/null +++ b/sproutcore/apps/fp/resources/d3.v3.js @@ -0,0 +1,7790 @@ +d3 = function() { + var π = Math.PI, ε = 1e-6, d3 = { + version: "3.0.6" + }, d3_radians = π / 180, d3_degrees = 180 / π, d3_document = document, d3_window = window; + function d3_target(d) { + return d.target; + } + function d3_source(d) { + return d.source; + } + var d3_format_decimalPoint = ".", d3_format_thousandsSeparator = ",", d3_format_grouping = [ 3, 3 ]; + if (!Date.now) Date.now = function() { + return +new Date(); + }; + try { + d3_document.createElement("div").style.setProperty("opacity", 0, ""); + } catch (error) { + var d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; + d3_style_prototype.setProperty = function(name, value, priority) { + d3_style_setProperty.call(this, name, value + "", priority); + }; + } + function d3_class(ctor, properties) { + try { + for (var key in properties) { + Object.defineProperty(ctor.prototype, key, { + value: properties[key], + enumerable: false + }); + } + } catch (e) { + ctor.prototype = properties; + } + } + var d3_array = d3_arraySlice; + function d3_arrayCopy(pseudoarray) { + var i = -1, n = pseudoarray.length, array = []; + while (++i < n) array.push(pseudoarray[i]); + return array; + } + function d3_arraySlice(pseudoarray) { + return Array.prototype.slice.call(pseudoarray); + } + try { + d3_array(d3_document.documentElement.childNodes)[0].nodeType; + } catch (e) { + d3_array = d3_arrayCopy; + } + var d3_arraySubclass = [].__proto__ ? function(array, prototype) { + array.__proto__ = prototype; + } : function(array, prototype) { + for (var property in prototype) array[property] = prototype[property]; + }; + d3.map = function(object) { + var map = new d3_Map(); + for (var key in object) map.set(key, object[key]); + return map; + }; + function d3_Map() {} + d3_class(d3_Map, { + has: function(key) { + return d3_map_prefix + key in this; + }, + get: function(key) { + return this[d3_map_prefix + key]; + }, + set: function(key, value) { + return this[d3_map_prefix + key] = value; + }, + remove: function(key) { + key = d3_map_prefix + key; + return key in this && delete this[key]; + }, + keys: function() { + var keys = []; + this.forEach(function(key) { + keys.push(key); + }); + return keys; + }, + values: function() { + var values = []; + this.forEach(function(key, value) { + values.push(value); + }); + return values; + }, + entries: function() { + var entries = []; + this.forEach(function(key, value) { + entries.push({ + key: key, + value: value + }); + }); + return entries; + }, + forEach: function(f) { + for (var key in this) { + if (key.charCodeAt(0) === d3_map_prefixCode) { + f.call(this, key.substring(1), this[key]); + } + } + } + }); + var d3_map_prefix = "\0", d3_map_prefixCode = d3_map_prefix.charCodeAt(0); + function d3_identity(d) { + return d; + } + function d3_true() { + return true; + } + function d3_functor(v) { + return typeof v === "function" ? v : function() { + return v; + }; + } + d3.functor = d3_functor; + d3.rebind = function(target, source) { + var i = 1, n = arguments.length, method; + while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); + return target; + }; + function d3_rebind(target, source, method) { + return function() { + var value = method.apply(source, arguments); + return arguments.length ? target : value; + }; + } + d3.ascending = function(a, b) { + return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; + }; + d3.descending = function(a, b) { + return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; + }; + d3.mean = function(array, f) { + var n = array.length, a, m = 0, i = -1, j = 0; + if (arguments.length === 1) { + while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j; + } else { + while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j; + } + return j ? m : undefined; + }; + d3.median = function(array, f) { + if (arguments.length > 1) array = array.map(f); + array = array.filter(d3_number); + return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined; + }; + d3.min = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n && ((a = array[i]) == null || a != a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && a > b) a = b; + } else { + while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; + } + return a; + }; + d3.max = function(array, f) { + var i = -1, n = array.length, a, b; + if (arguments.length === 1) { + while (++i < n && ((a = array[i]) == null || a != a)) a = undefined; + while (++i < n) if ((b = array[i]) != null && b > a) a = b; + } else { + while (++i < n && ((a = f.call(array, array[i], i)) == null || a != a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; + } + return a; + }; + d3.extent = function(array, f) { + var i = -1, n = array.length, a, b, c; + if (arguments.length === 1) { + while (++i < n && ((a = c = array[i]) == null || a != a)) a = c = undefined; + while (++i < n) if ((b = array[i]) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } else { + while (++i < n && ((a = c = f.call(array, array[i], i)) == null || a != a)) a = undefined; + while (++i < n) if ((b = f.call(array, array[i], i)) != null) { + if (a > b) a = b; + if (c < b) c = b; + } + } + return [ a, c ]; + }; + d3.random = { + normal: function(µ, σ) { + var n = arguments.length; + if (n < 2) σ = 1; + if (n < 1) µ = 0; + return function() { + var x, y, r; + do { + x = Math.random() * 2 - 1; + y = Math.random() * 2 - 1; + r = x * x + y * y; + } while (!r || r > 1); + return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); + }; + }, + logNormal: function() { + var random = d3.random.normal.apply(d3, arguments); + return function() { + return Math.exp(random()); + }; + }, + irwinHall: function(m) { + return function() { + for (var s = 0, j = 0; j < m; j++) s += Math.random(); + return s / m; + }; + } + }; + function d3_number(x) { + return x != null && !isNaN(x); + } + d3.sum = function(array, f) { + var s = 0, n = array.length, a, i = -1; + if (arguments.length === 1) { + while (++i < n) if (!isNaN(a = +array[i])) s += a; + } else { + while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a; + } + return s; + }; + d3.quantile = function(values, p) { + var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; + return e ? v + e * (values[h] - v) : v; + }; + d3.shuffle = function(array) { + var m = array.length, t, i; + while (m) { + i = Math.random() * m-- | 0; + t = array[m], array[m] = array[i], array[i] = t; + } + return array; + }; + d3.transpose = function(matrix) { + return d3.zip.apply(d3, matrix); + }; + d3.zip = function() { + if (!(n = arguments.length)) return []; + for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { + for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { + zip[j] = arguments[j][i]; + } + } + return zips; + }; + function d3_zipLength(d) { + return d.length; + } + d3.bisector = function(f) { + return { + left: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (f.call(a, a[mid], mid) < x) lo = mid + 1; else hi = mid; + } + return lo; + }, + right: function(a, x, lo, hi) { + if (arguments.length < 3) lo = 0; + if (arguments.length < 4) hi = a.length; + while (lo < hi) { + var mid = lo + hi >>> 1; + if (x < f.call(a, a[mid], mid)) hi = mid; else lo = mid + 1; + } + return lo; + } + }; + }; + var d3_bisector = d3.bisector(function(d) { + return d; + }); + d3.bisectLeft = d3_bisector.left; + d3.bisect = d3.bisectRight = d3_bisector.right; + d3.nest = function() { + var nest = {}, keys = [], sortKeys = [], sortValues, rollup; + function map(array, depth) { + if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; + var i = -1, n = array.length, key = keys[depth++], keyValue, object, valuesByKey = new d3_Map(), values, o = {}; + while (++i < n) { + if (values = valuesByKey.get(keyValue = key(object = array[i]))) { + values.push(object); + } else { + valuesByKey.set(keyValue, [ object ]); + } + } + valuesByKey.forEach(function(keyValue, values) { + o[keyValue] = map(values, depth); + }); + return o; + } + function entries(map, depth) { + if (depth >= keys.length) return map; + var a = [], sortKey = sortKeys[depth++], key; + for (key in map) { + a.push({ + key: key, + values: entries(map[key], depth) + }); + } + if (sortKey) a.sort(function(a, b) { + return sortKey(a.key, b.key); + }); + return a; + } + nest.map = function(array) { + return map(array, 0); + }; + nest.entries = function(array) { + return entries(map(array, 0), 0); + }; + nest.key = function(d) { + keys.push(d); + return nest; + }; + nest.sortKeys = function(order) { + sortKeys[keys.length - 1] = order; + return nest; + }; + nest.sortValues = function(order) { + sortValues = order; + return nest; + }; + nest.rollup = function(f) { + rollup = f; + return nest; + }; + return nest; + }; + d3.keys = function(map) { + var keys = []; + for (var key in map) keys.push(key); + return keys; + }; + d3.values = function(map) { + var values = []; + for (var key in map) values.push(map[key]); + return values; + }; + d3.entries = function(map) { + var entries = []; + for (var key in map) entries.push({ + key: key, + value: map[key] + }); + return entries; + }; + d3.permute = function(array, indexes) { + var permutes = [], i = -1, n = indexes.length; + while (++i < n) permutes[i] = array[indexes[i]]; + return permutes; + }; + d3.merge = function(arrays) { + return Array.prototype.concat.apply([], arrays); + }; + function d3_collapse(s) { + return s.trim().replace(/\s+/g, " "); + } + d3.range = function(start, stop, step) { + if (arguments.length < 3) { + step = 1; + if (arguments.length < 2) { + stop = start; + start = 0; + } + } + if ((stop - start) / step === Infinity) throw new Error("infinite range"); + var range = [], k = d3_range_integerScale(Math.abs(step)), i = -1, j; + start *= k, stop *= k, step *= k; + if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); + return range; + }; + function d3_range_integerScale(x) { + var k = 1; + while (x * k % 1) k *= 10; + return k; + } + d3.requote = function(s) { + return s.replace(d3_requote_re, "\\$&"); + }; + var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; + d3.round = function(x, n) { + return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); + }; + d3.xhr = function(url, mimeType, callback) { + var xhr = {}, dispatch = d3.dispatch("progress", "load", "error"), headers = {}, response = d3_identity, request = new (d3_window.XDomainRequest && /^(http(s)?:)?\/\//.test(url) ? XDomainRequest : XMLHttpRequest)(); + "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { + request.readyState > 3 && respond(); + }; + function respond() { + var s = request.status; + !s && request.responseText || s >= 200 && s < 300 || s === 304 ? dispatch.load.call(xhr, response.call(xhr, request)) : dispatch.error.call(xhr, request); + } + request.onprogress = function(event) { + var o = d3.event; + d3.event = event; + try { + dispatch.progress.call(xhr, request); + } finally { + d3.event = o; + } + }; + xhr.header = function(name, value) { + name = (name + "").toLowerCase(); + if (arguments.length < 2) return headers[name]; + if (value == null) delete headers[name]; else headers[name] = value + ""; + return xhr; + }; + xhr.mimeType = function(value) { + if (!arguments.length) return mimeType; + mimeType = value == null ? null : value + ""; + return xhr; + }; + xhr.response = function(value) { + response = value; + return xhr; + }; + [ "get", "post" ].forEach(function(method) { + xhr[method] = function() { + return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); + }; + }); + xhr.send = function(method, data, callback) { + if (arguments.length === 2 && typeof data === "function") callback = data, data = null; + request.open(method, url, true); + if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; + if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); + if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); + if (callback != null) xhr.on("error", callback).on("load", function(request) { + callback(null, request); + }); + request.send(data == null ? null : data); + return xhr; + }; + xhr.abort = function() { + request.abort(); + return xhr; + }; + d3.rebind(xhr, dispatch, "on"); + if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, + mimeType = null; + return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); + }; + function d3_xhr_fixCallback(callback) { + return callback.length === 1 ? function(error, request) { + callback(error == null ? request : null); + } : callback; + } + d3.text = function() { + return d3.xhr.apply(d3, arguments).response(d3_text); + }; + function d3_text(request) { + return request.responseText; + } + d3.json = function(url, callback) { + return d3.xhr(url, "application/json", callback).response(d3_json); + }; + function d3_json(request) { + return JSON.parse(request.responseText); + } + d3.html = function(url, callback) { + return d3.xhr(url, "text/html", callback).response(d3_html); + }; + function d3_html(request) { + var range = d3_document.createRange(); + range.selectNode(d3_document.body); + return range.createContextualFragment(request.responseText); + } + d3.xml = function() { + return d3.xhr.apply(d3, arguments).response(d3_xml); + }; + function d3_xml(request) { + return request.responseXML; + } + var d3_nsPrefix = { + svg: "http://www.w3.org/2000/svg", + xhtml: "http://www.w3.org/1999/xhtml", + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" + }; + d3.ns = { + prefix: d3_nsPrefix, + qualify: function(name) { + var i = name.indexOf(":"), prefix = name; + if (i >= 0) { + prefix = name.substring(0, i); + name = name.substring(i + 1); + } + return d3_nsPrefix.hasOwnProperty(prefix) ? { + space: d3_nsPrefix[prefix], + local: name + } : name; + } + }; + d3.dispatch = function() { + var dispatch = new d3_dispatch(), i = -1, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + return dispatch; + }; + function d3_dispatch() {} + d3_dispatch.prototype.on = function(type, listener) { + var i = type.indexOf("."), name = ""; + if (i > 0) { + name = type.substring(i + 1); + type = type.substring(0, i); + } + return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); + }; + function d3_dispatch_event(dispatch) { + var listeners = [], listenerByName = new d3_Map(); + function event() { + var z = listeners, i = -1, n = z.length, l; + while (++i < n) if (l = z[i].on) l.apply(this, arguments); + return dispatch; + } + event.on = function(name, listener) { + var l = listenerByName.get(name), i; + if (arguments.length < 2) return l && l.on; + if (l) { + l.on = null; + listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); + listenerByName.remove(name); + } + if (listener) listeners.push(listenerByName.set(name, { + on: listener + })); + return dispatch; + }; + return event; + } + d3.format = function(specifier) { + var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", basePrefix = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, suffix = "", integer = false; + if (precision) precision = +precision.substring(1); + if (zfill || fill === "0" && align === "=") { + zfill = fill = "0"; + align = "="; + if (comma) width -= Math.floor((width - 1) / 4); + } + switch (type) { + case "n": + comma = true; + type = "g"; + break; + + case "%": + scale = 100; + suffix = "%"; + type = "f"; + break; + + case "p": + scale = 100; + suffix = "%"; + type = "r"; + break; + + case "b": + case "o": + case "x": + case "X": + if (basePrefix) basePrefix = "0" + type.toLowerCase(); + + case "c": + case "d": + integer = true; + precision = 0; + break; + + case "s": + scale = -1; + type = "r"; + break; + } + if (basePrefix === "#") basePrefix = ""; + if (type == "r" && !precision) type = "g"; + type = d3_format_types.get(type) || d3_format_typeDefault; + var zcomma = zfill && comma; + return function(value) { + if (integer && value % 1) return ""; + var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign; + if (scale < 0) { + var prefix = d3.formatPrefix(value, precision); + value = prefix.scale(value); + suffix = prefix.symbol; + } else { + value *= scale; + } + value = type(value, precision); + if (!zfill && comma) value = d3_format_group(value); + var length = basePrefix.length + value.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; + if (zcomma) value = d3_format_group(padding + value); + if (d3_format_decimalPoint) value.replace(".", d3_format_decimalPoint); + negative += basePrefix; + return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + suffix; + }; + }; + var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?(#)?(0)?([0-9]+)?(,)?(\.[0-9]+)?([a-zA-Z%])?/; + var d3_format_types = d3.map({ + b: function(x) { + return x.toString(2); + }, + c: function(x) { + return String.fromCharCode(x); + }, + o: function(x) { + return x.toString(8); + }, + x: function(x) { + return x.toString(16); + }, + X: function(x) { + return x.toString(16).toUpperCase(); + }, + g: function(x, p) { + return x.toPrecision(p); + }, + e: function(x, p) { + return x.toExponential(p); + }, + f: function(x, p) { + return x.toFixed(p); + }, + r: function(x, p) { + return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); + } + }); + function d3_format_precision(x, p) { + return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); + } + function d3_format_typeDefault(x) { + return x + ""; + } + var d3_format_group = d3_identity; + if (d3_format_grouping) { + var d3_format_groupingLength = d3_format_grouping.length; + d3_format_group = function(value) { + var i = value.lastIndexOf("."), f = i >= 0 ? "." + value.substring(i + 1) : (i = value.length, + ""), t = [], j = 0, g = d3_format_grouping[0]; + while (i > 0 && g > 0) { + t.push(value.substring(i -= g, i + g)); + g = d3_format_grouping[j = (j + 1) % d3_format_groupingLength]; + } + return t.reverse().join(d3_format_thousandsSeparator || "") + f; + }; + } + var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); + d3.formatPrefix = function(value, precision) { + var i = 0; + if (value) { + if (value < 0) value *= -1; + if (precision) value = d3.round(value, d3_format_precision(value, precision)); + i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); + i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3)); + } + return d3_formatPrefixes[8 + i / 3]; + }; + function d3_formatPrefix(d, i) { + var k = Math.pow(10, Math.abs(8 - i) * 3); + return { + scale: i > 8 ? function(d) { + return d / k; + } : function(d) { + return d * k; + }, + symbol: d + }; + } + var d3_ease_default = function() { + return d3_identity; + }; + var d3_ease = d3.map({ + linear: d3_ease_default, + poly: d3_ease_poly, + quad: function() { + return d3_ease_quad; + }, + cubic: function() { + return d3_ease_cubic; + }, + sin: function() { + return d3_ease_sin; + }, + exp: function() { + return d3_ease_exp; + }, + circle: function() { + return d3_ease_circle; + }, + elastic: d3_ease_elastic, + back: d3_ease_back, + bounce: function() { + return d3_ease_bounce; + } + }); + var d3_ease_mode = d3.map({ + "in": d3_identity, + out: d3_ease_reverse, + "in-out": d3_ease_reflect, + "out-in": function(f) { + return d3_ease_reflect(d3_ease_reverse(f)); + } + }); + d3.ease = function(name) { + var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in"; + t = d3_ease.get(t) || d3_ease_default; + m = d3_ease_mode.get(m) || d3_identity; + return d3_ease_clamp(m(t.apply(null, Array.prototype.slice.call(arguments, 1)))); + }; + function d3_ease_clamp(f) { + return function(t) { + return t <= 0 ? 0 : t >= 1 ? 1 : f(t); + }; + } + function d3_ease_reverse(f) { + return function(t) { + return 1 - f(1 - t); + }; + } + function d3_ease_reflect(f) { + return function(t) { + return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); + }; + } + function d3_ease_quad(t) { + return t * t; + } + function d3_ease_cubic(t) { + return t * t * t; + } + function d3_ease_cubicInOut(t) { + if (t <= 0) return 0; + if (t >= 1) return 1; + var t2 = t * t, t3 = t2 * t; + return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); + } + function d3_ease_poly(e) { + return function(t) { + return Math.pow(t, e); + }; + } + function d3_ease_sin(t) { + return 1 - Math.cos(t * π / 2); + } + function d3_ease_exp(t) { + return Math.pow(2, 10 * (t - 1)); + } + function d3_ease_circle(t) { + return 1 - Math.sqrt(1 - t * t); + } + function d3_ease_elastic(a, p) { + var s; + if (arguments.length < 2) p = .45; + if (arguments.length) s = p / (2 * π) * Math.asin(1 / a); else a = 1, s = p / 4; + return function(t) { + return 1 + a * Math.pow(2, 10 * -t) * Math.sin((t - s) * 2 * π / p); + }; + } + function d3_ease_back(s) { + if (!s) s = 1.70158; + return function(t) { + return t * t * ((s + 1) * t - s); + }; + } + function d3_ease_bounce(t) { + return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; + } + d3.event = null; + function d3_eventCancel() { + d3.event.stopPropagation(); + d3.event.preventDefault(); + } + function d3_eventSource() { + var e = d3.event, s; + while (s = e.sourceEvent) e = s; + return e; + } + function d3_eventDispatch(target) { + var dispatch = new d3_dispatch(), i = 0, n = arguments.length; + while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); + dispatch.of = function(thiz, argumentz) { + return function(e1) { + try { + var e0 = e1.sourceEvent = d3.event; + e1.target = target; + d3.event = e1; + dispatch[e1.type].apply(thiz, argumentz); + } finally { + d3.event = e0; + } + }; + }; + return dispatch; + } + d3.transform = function(string) { + var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); + return (d3.transform = function(string) { + g.setAttribute("transform", string); + var t = g.transform.baseVal.consolidate(); + return new d3_transform(t ? t.matrix : d3_transformIdentity); + })(string); + }; + function d3_transform(m) { + var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; + if (r0[0] * r1[1] < r1[0] * r0[1]) { + r0[0] *= -1; + r0[1] *= -1; + kx *= -1; + kz *= -1; + } + this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; + this.translate = [ m.e, m.f ]; + this.scale = [ kx, ky ]; + this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; + } + d3_transform.prototype.toString = function() { + return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; + }; + function d3_transformDot(a, b) { + return a[0] * b[0] + a[1] * b[1]; + } + function d3_transformNormalize(a) { + var k = Math.sqrt(d3_transformDot(a, a)); + if (k) { + a[0] /= k; + a[1] /= k; + } + return k; + } + function d3_transformCombine(a, b, k) { + a[0] += k * b[0]; + a[1] += k * b[1]; + return a; + } + var d3_transformIdentity = { + a: 1, + b: 0, + c: 0, + d: 1, + e: 0, + f: 0 + }; + d3.interpolate = function(a, b) { + var i = d3.interpolators.length, f; + while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; + return f; + }; + d3.interpolateNumber = function(a, b) { + b -= a; + return function(t) { + return a + b * t; + }; + }; + d3.interpolateRound = function(a, b) { + b -= a; + return function(t) { + return Math.round(a + b * t); + }; + }; + d3.interpolateString = function(a, b) { + var m, i, j, s0 = 0, s1 = 0, s = [], q = [], n, o; + d3_interpolate_number.lastIndex = 0; + for (i = 0; m = d3_interpolate_number.exec(b); ++i) { + if (m.index) s.push(b.substring(s0, s1 = m.index)); + q.push({ + i: s.length, + x: m[0] + }); + s.push(null); + s0 = d3_interpolate_number.lastIndex; + } + if (s0 < b.length) s.push(b.substring(s0)); + for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) { + o = q[i]; + if (o.x == m[0]) { + if (o.i) { + if (s[o.i + 1] == null) { + s[o.i - 1] += o.x; + s.splice(o.i, 1); + for (j = i + 1; j < n; ++j) q[j].i--; + } else { + s[o.i - 1] += o.x + s[o.i + 1]; + s.splice(o.i, 2); + for (j = i + 1; j < n; ++j) q[j].i -= 2; + } + } else { + if (s[o.i + 1] == null) { + s[o.i] = o.x; + } else { + s[o.i] = o.x + s[o.i + 1]; + s.splice(o.i + 1, 1); + for (j = i + 1; j < n; ++j) q[j].i--; + } + } + q.splice(i, 1); + n--; + i--; + } else { + o.x = d3.interpolateNumber(parseFloat(m[0]), parseFloat(o.x)); + } + } + while (i < n) { + o = q.pop(); + if (s[o.i + 1] == null) { + s[o.i] = o.x; + } else { + s[o.i] = o.x + s[o.i + 1]; + s.splice(o.i + 1, 1); + } + n--; + } + if (s.length === 1) { + return s[0] == null ? q[0].x : function() { + return b; + }; + } + return function(t) { + for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + }; + d3.interpolateTransform = function(a, b) { + var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale; + if (ta[0] != tb[0] || ta[1] != tb[1]) { + s.push("translate(", null, ",", null, ")"); + q.push({ + i: 1, + x: d3.interpolateNumber(ta[0], tb[0]) + }, { + i: 3, + x: d3.interpolateNumber(ta[1], tb[1]) + }); + } else if (tb[0] || tb[1]) { + s.push("translate(" + tb + ")"); + } else { + s.push(""); + } + if (ra != rb) { + if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; + q.push({ + i: s.push(s.pop() + "rotate(", null, ")") - 2, + x: d3.interpolateNumber(ra, rb) + }); + } else if (rb) { + s.push(s.pop() + "rotate(" + rb + ")"); + } + if (wa != wb) { + q.push({ + i: s.push(s.pop() + "skewX(", null, ")") - 2, + x: d3.interpolateNumber(wa, wb) + }); + } else if (wb) { + s.push(s.pop() + "skewX(" + wb + ")"); + } + if (ka[0] != kb[0] || ka[1] != kb[1]) { + n = s.push(s.pop() + "scale(", null, ",", null, ")"); + q.push({ + i: n - 4, + x: d3.interpolateNumber(ka[0], kb[0]) + }, { + i: n - 2, + x: d3.interpolateNumber(ka[1], kb[1]) + }); + } else if (kb[0] != 1 || kb[1] != 1) { + s.push(s.pop() + "scale(" + kb + ")"); + } + n = q.length; + return function(t) { + var i = -1, o; + while (++i < n) s[(o = q[i]).i] = o.x(t); + return s.join(""); + }; + }; + d3.interpolateRgb = function(a, b) { + a = d3.rgb(a); + b = d3.rgb(b); + var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; + return function(t) { + return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); + }; + }; + d3.interpolateHsl = function(a, b) { + a = d3.hsl(a); + b = d3.hsl(b); + var h0 = a.h, s0 = a.s, l0 = a.l, h1 = b.h - h0, s1 = b.s - s0, l1 = b.l - l0; + if (h1 > 180) h1 -= 360; else if (h1 < -180) h1 += 360; + return function(t) { + return d3_hsl_rgb(h0 + h1 * t, s0 + s1 * t, l0 + l1 * t) + ""; + }; + }; + d3.interpolateLab = function(a, b) { + a = d3.lab(a); + b = d3.lab(b); + var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; + return function(t) { + return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; + }; + }; + d3.interpolateHcl = function(a, b) { + a = d3.hcl(a); + b = d3.hcl(b); + var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; + if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; + return function(t) { + return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; + }; + }; + d3.interpolateArray = function(a, b) { + var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; + for (i = 0; i < n0; ++i) x.push(d3.interpolate(a[i], b[i])); + for (;i < na; ++i) c[i] = a[i]; + for (;i < nb; ++i) c[i] = b[i]; + return function(t) { + for (i = 0; i < n0; ++i) c[i] = x[i](t); + return c; + }; + }; + d3.interpolateObject = function(a, b) { + var i = {}, c = {}, k; + for (k in a) { + if (k in b) { + i[k] = d3_interpolateByName(k)(a[k], b[k]); + } else { + c[k] = a[k]; + } + } + for (k in b) { + if (!(k in a)) { + c[k] = b[k]; + } + } + return function(t) { + for (k in i) c[k] = i[k](t); + return c; + }; + }; + var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g; + function d3_interpolateByName(name) { + return name == "transform" ? d3.interpolateTransform : d3.interpolate; + } + d3.interpolators = [ d3.interpolateObject, function(a, b) { + return b instanceof Array && d3.interpolateArray(a, b); + }, function(a, b) { + return (typeof a === "string" || typeof b === "string") && d3.interpolateString(a + "", b + ""); + }, function(a, b) { + return (typeof b === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) : b instanceof d3_Color) && d3.interpolateRgb(a, b); + }, function(a, b) { + return !isNaN(a = +a) && !isNaN(b = +b) && d3.interpolateNumber(a, b); + } ]; + function d3_uninterpolateNumber(a, b) { + b = b - (a = +a) ? 1 / (b - a) : 0; + return function(x) { + return (x - a) * b; + }; + } + function d3_uninterpolateClamp(a, b) { + b = b - (a = +a) ? 1 / (b - a) : 0; + return function(x) { + return Math.max(0, Math.min(1, (x - a) * b)); + }; + } + function d3_Color() {} + d3_Color.prototype.toString = function() { + return this.rgb() + ""; + }; + d3.rgb = function(r, g, b) { + return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b); + }; + function d3_rgb(r, g, b) { + return new d3_Rgb(r, g, b); + } + function d3_Rgb(r, g, b) { + this.r = r; + this.g = g; + this.b = b; + } + var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color(); + d3_rgbPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + var r = this.r, g = this.g, b = this.b, i = 30; + if (!r && !g && !b) return d3_rgb(i, i, i); + if (r && r < i) r = i; + if (g && g < i) g = i; + if (b && b < i) b = i; + return d3_rgb(Math.min(255, Math.floor(r / k)), Math.min(255, Math.floor(g / k)), Math.min(255, Math.floor(b / k))); + }; + d3_rgbPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_rgb(Math.floor(k * this.r), Math.floor(k * this.g), Math.floor(k * this.b)); + }; + d3_rgbPrototype.hsl = function() { + return d3_rgb_hsl(this.r, this.g, this.b); + }; + d3_rgbPrototype.toString = function() { + return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); + }; + function d3_rgb_hex(v) { + return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); + } + function d3_rgb_parse(format, rgb, hsl) { + var r = 0, g = 0, b = 0, m1, m2, name; + m1 = /([a-z]+)\((.*)\)/i.exec(format); + if (m1) { + m2 = m1[2].split(","); + switch (m1[1]) { + case "hsl": + { + return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); + } + + case "rgb": + { + return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); + } + } + } + if (name = d3_rgb_names.get(format)) return rgb(name.r, name.g, name.b); + if (format != null && format.charAt(0) === "#") { + if (format.length === 4) { + r = format.charAt(1); + r += r; + g = format.charAt(2); + g += g; + b = format.charAt(3); + b += b; + } else if (format.length === 7) { + r = format.substring(1, 3); + g = format.substring(3, 5); + b = format.substring(5, 7); + } + r = parseInt(r, 16); + g = parseInt(g, 16); + b = parseInt(b, 16); + } + return rgb(r, g, b); + } + function d3_rgb_hsl(r, g, b) { + var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; + if (d) { + s = l < .5 ? d / (max + min) : d / (2 - max - min); + if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; + h *= 60; + } else { + s = h = 0; + } + return d3_hsl(h, s, l); + } + function d3_rgb_lab(r, g, b) { + r = d3_rgb_xyz(r); + g = d3_rgb_xyz(g); + b = d3_rgb_xyz(b); + var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); + return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); + } + function d3_rgb_xyz(r) { + return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); + } + function d3_rgb_parseNumber(c) { + var f = parseFloat(c); + return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; + } + var d3_rgb_names = d3.map({ + aliceblue: "#f0f8ff", + antiquewhite: "#faebd7", + aqua: "#00ffff", + aquamarine: "#7fffd4", + azure: "#f0ffff", + beige: "#f5f5dc", + bisque: "#ffe4c4", + black: "#000000", + blanchedalmond: "#ffebcd", + blue: "#0000ff", + blueviolet: "#8a2be2", + brown: "#a52a2a", + burlywood: "#deb887", + cadetblue: "#5f9ea0", + chartreuse: "#7fff00", + chocolate: "#d2691e", + coral: "#ff7f50", + cornflowerblue: "#6495ed", + cornsilk: "#fff8dc", + crimson: "#dc143c", + cyan: "#00ffff", + darkblue: "#00008b", + darkcyan: "#008b8b", + darkgoldenrod: "#b8860b", + darkgray: "#a9a9a9", + darkgreen: "#006400", + darkgrey: "#a9a9a9", + darkkhaki: "#bdb76b", + darkmagenta: "#8b008b", + darkolivegreen: "#556b2f", + darkorange: "#ff8c00", + darkorchid: "#9932cc", + darkred: "#8b0000", + darksalmon: "#e9967a", + darkseagreen: "#8fbc8f", + darkslateblue: "#483d8b", + darkslategray: "#2f4f4f", + darkslategrey: "#2f4f4f", + darkturquoise: "#00ced1", + darkviolet: "#9400d3", + deeppink: "#ff1493", + deepskyblue: "#00bfff", + dimgray: "#696969", + dimgrey: "#696969", + dodgerblue: "#1e90ff", + firebrick: "#b22222", + floralwhite: "#fffaf0", + forestgreen: "#228b22", + fuchsia: "#ff00ff", + gainsboro: "#dcdcdc", + ghostwhite: "#f8f8ff", + gold: "#ffd700", + goldenrod: "#daa520", + gray: "#808080", + green: "#008000", + greenyellow: "#adff2f", + grey: "#808080", + honeydew: "#f0fff0", + hotpink: "#ff69b4", + indianred: "#cd5c5c", + indigo: "#4b0082", + ivory: "#fffff0", + khaki: "#f0e68c", + lavender: "#e6e6fa", + lavenderblush: "#fff0f5", + lawngreen: "#7cfc00", + lemonchiffon: "#fffacd", + lightblue: "#add8e6", + lightcoral: "#f08080", + lightcyan: "#e0ffff", + lightgoldenrodyellow: "#fafad2", + lightgray: "#d3d3d3", + lightgreen: "#90ee90", + lightgrey: "#d3d3d3", + lightpink: "#ffb6c1", + lightsalmon: "#ffa07a", + lightseagreen: "#20b2aa", + lightskyblue: "#87cefa", + lightslategray: "#778899", + lightslategrey: "#778899", + lightsteelblue: "#b0c4de", + lightyellow: "#ffffe0", + lime: "#00ff00", + limegreen: "#32cd32", + linen: "#faf0e6", + magenta: "#ff00ff", + maroon: "#800000", + mediumaquamarine: "#66cdaa", + mediumblue: "#0000cd", + mediumorchid: "#ba55d3", + mediumpurple: "#9370db", + mediumseagreen: "#3cb371", + mediumslateblue: "#7b68ee", + mediumspringgreen: "#00fa9a", + mediumturquoise: "#48d1cc", + mediumvioletred: "#c71585", + midnightblue: "#191970", + mintcream: "#f5fffa", + mistyrose: "#ffe4e1", + moccasin: "#ffe4b5", + navajowhite: "#ffdead", + navy: "#000080", + oldlace: "#fdf5e6", + olive: "#808000", + olivedrab: "#6b8e23", + orange: "#ffa500", + orangered: "#ff4500", + orchid: "#da70d6", + palegoldenrod: "#eee8aa", + palegreen: "#98fb98", + paleturquoise: "#afeeee", + palevioletred: "#db7093", + papayawhip: "#ffefd5", + peachpuff: "#ffdab9", + peru: "#cd853f", + pink: "#ffc0cb", + plum: "#dda0dd", + powderblue: "#b0e0e6", + purple: "#800080", + red: "#ff0000", + rosybrown: "#bc8f8f", + royalblue: "#4169e1", + saddlebrown: "#8b4513", + salmon: "#fa8072", + sandybrown: "#f4a460", + seagreen: "#2e8b57", + seashell: "#fff5ee", + sienna: "#a0522d", + silver: "#c0c0c0", + skyblue: "#87ceeb", + slateblue: "#6a5acd", + slategray: "#708090", + slategrey: "#708090", + snow: "#fffafa", + springgreen: "#00ff7f", + steelblue: "#4682b4", + tan: "#d2b48c", + teal: "#008080", + thistle: "#d8bfd8", + tomato: "#ff6347", + turquoise: "#40e0d0", + violet: "#ee82ee", + wheat: "#f5deb3", + white: "#ffffff", + whitesmoke: "#f5f5f5", + yellow: "#ffff00", + yellowgreen: "#9acd32" + }); + d3_rgb_names.forEach(function(key, value) { + d3_rgb_names.set(key, d3_rgb_parse(value, d3_rgb, d3_hsl_rgb)); + }); + d3.hsl = function(h, s, l) { + return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l); + }; + function d3_hsl(h, s, l) { + return new d3_Hsl(h, s, l); + } + function d3_Hsl(h, s, l) { + this.h = h; + this.s = s; + this.l = l; + } + var d3_hslPrototype = d3_Hsl.prototype = new d3_Color(); + d3_hslPrototype.brighter = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_hsl(this.h, this.s, this.l / k); + }; + d3_hslPrototype.darker = function(k) { + k = Math.pow(.7, arguments.length ? k : 1); + return d3_hsl(this.h, this.s, k * this.l); + }; + d3_hslPrototype.rgb = function() { + return d3_hsl_rgb(this.h, this.s, this.l); + }; + function d3_hsl_rgb(h, s, l) { + var m1, m2; + h = h % 360; + if (h < 0) h += 360; + s = s < 0 ? 0 : s > 1 ? 1 : s; + l = l < 0 ? 0 : l > 1 ? 1 : l; + m2 = l <= .5 ? l * (1 + s) : l + s - l * s; + m1 = 2 * l - m2; + function v(h) { + if (h > 360) h -= 360; else if (h < 0) h += 360; + if (h < 60) return m1 + (m2 - m1) * h / 60; + if (h < 180) return m2; + if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; + return m1; + } + function vv(h) { + return Math.round(v(h) * 255); + } + return d3_rgb(vv(h + 120), vv(h), vv(h - 120)); + } + d3.hcl = function(h, c, l) { + return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l); + }; + function d3_hcl(h, c, l) { + return new d3_Hcl(h, c, l); + } + function d3_Hcl(h, c, l) { + this.h = h; + this.c = c; + this.l = l; + } + var d3_hclPrototype = d3_Hcl.prototype = new d3_Color(); + d3_hclPrototype.brighter = function(k) { + return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.darker = function(k) { + return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); + }; + d3_hclPrototype.rgb = function() { + return d3_hcl_lab(this.h, this.c, this.l).rgb(); + }; + function d3_hcl_lab(h, c, l) { + return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); + } + d3.lab = function(l, a, b) { + return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b); + }; + function d3_lab(l, a, b) { + return new d3_Lab(l, a, b); + } + function d3_Lab(l, a, b) { + this.l = l; + this.a = a; + this.b = b; + } + var d3_lab_K = 18; + var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; + var d3_labPrototype = d3_Lab.prototype = new d3_Color(); + d3_labPrototype.brighter = function(k) { + return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.darker = function(k) { + return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); + }; + d3_labPrototype.rgb = function() { + return d3_lab_rgb(this.l, this.a, this.b); + }; + function d3_lab_rgb(l, a, b) { + var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; + x = d3_lab_xyz(x) * d3_lab_X; + y = d3_lab_xyz(y) * d3_lab_Y; + z = d3_lab_xyz(z) * d3_lab_Z; + return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); + } + function d3_lab_hcl(l, a, b) { + return d3_hcl(Math.atan2(b, a) / π * 180, Math.sqrt(a * a + b * b), l); + } + function d3_lab_xyz(x) { + return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; + } + function d3_xyz_lab(x) { + return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; + } + function d3_xyz_rgb(r) { + return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); + } + function d3_selection(groups) { + d3_arraySubclass(groups, d3_selectionPrototype); + return groups; + } + var d3_select = function(s, n) { + return n.querySelector(s); + }, d3_selectAll = function(s, n) { + return n.querySelectorAll(s); + }, d3_selectRoot = d3_document.documentElement, d3_selectMatcher = d3_selectRoot.matchesSelector || d3_selectRoot.webkitMatchesSelector || d3_selectRoot.mozMatchesSelector || d3_selectRoot.msMatchesSelector || d3_selectRoot.oMatchesSelector, d3_selectMatches = function(n, s) { + return d3_selectMatcher.call(n, s); + }; + if (typeof Sizzle === "function") { + d3_select = function(s, n) { + return Sizzle(s, n)[0] || null; + }; + d3_selectAll = function(s, n) { + return Sizzle.uniqueSort(Sizzle(s, n)); + }; + d3_selectMatches = Sizzle.matchesSelector; + } + var d3_selectionPrototype = []; + d3.selection = function() { + return d3_selectionRoot; + }; + d3.selection.prototype = d3_selectionPrototype; + d3_selectionPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, group, node; + if (typeof selector !== "function") selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(subnode = selector.call(node, node.__data__, i)); + if (subnode && "__data__" in node) subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selector(selector) { + return function() { + return d3_select(selector, this); + }; + } + d3_selectionPrototype.selectAll = function(selector) { + var subgroups = [], subgroup, node; + if (typeof selector !== "function") selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i))); + subgroup.parentNode = node; + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_selectorAll(selector) { + return function() { + return d3_selectAll(selector, this); + }; + } + d3_selectionPrototype.attr = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(); + name = d3.ns.qualify(name); + return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); + } + for (value in name) this.each(d3_selection_attr(value, name[value])); + return this; + } + return this.each(d3_selection_attr(name, value)); + }; + function d3_selection_attr(name, value) { + name = d3.ns.qualify(name); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + function attrConstant() { + this.setAttribute(name, value); + } + function attrConstantNS() { + this.setAttributeNS(name.space, name.local, value); + } + function attrFunction() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); + } + function attrFunctionNS() { + var x = value.apply(this, arguments); + if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); + } + return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; + } + d3_selectionPrototype.classed = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") { + var node = this.node(), n = (name = name.trim().split(/^|\s+/g)).length, i = -1; + if (value = node.classList) { + while (++i < n) if (!value.contains(name[i])) return false; + } else { + value = node.className; + if (value.baseVal != null) value = value.baseVal; + while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; + } + return true; + } + for (value in name) this.each(d3_selection_classed(value, name[value])); + return this; + } + return this.each(d3_selection_classed(name, value)); + }; + function d3_selection_classedRe(name) { + return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); + } + function d3_selection_classed(name, value) { + name = name.trim().split(/\s+/).map(d3_selection_classedName); + var n = name.length; + function classedConstant() { + var i = -1; + while (++i < n) name[i](this, value); + } + function classedFunction() { + var i = -1, x = value.apply(this, arguments); + while (++i < n) name[i](this, x); + } + return typeof value === "function" ? classedFunction : classedConstant; + } + function d3_selection_classedName(name) { + var re = d3_selection_classedRe(name); + return function(node, value) { + if (c = node.classList) return value ? c.add(name) : c.remove(name); + var c = node.className, cb = c.baseVal != null, cv = cb ? c.baseVal : c; + if (value) { + re.lastIndex = 0; + if (!re.test(cv)) { + cv = d3_collapse(cv + " " + name); + if (cb) c.baseVal = cv; else node.className = cv; + } + } else if (cv) { + cv = d3_collapse(cv.replace(re, " ")); + if (cb) c.baseVal = cv; else node.className = cv; + } + }; + } + d3_selectionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); + return this; + } + if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name); + priority = ""; + } + return this.each(d3_selection_style(name, value, priority)); + }; + function d3_selection_style(name, value, priority) { + function styleNull() { + this.style.removeProperty(name); + } + function styleConstant() { + this.style.setProperty(name, value, priority); + } + function styleFunction() { + var x = value.apply(this, arguments); + if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); + } + return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; + } + d3_selectionPrototype.property = function(name, value) { + if (arguments.length < 2) { + if (typeof name === "string") return this.node()[name]; + for (value in name) this.each(d3_selection_property(value, name[value])); + return this; + } + return this.each(d3_selection_property(name, value)); + }; + function d3_selection_property(name, value) { + function propertyNull() { + delete this[name]; + } + function propertyConstant() { + this[name] = value; + } + function propertyFunction() { + var x = value.apply(this, arguments); + if (x == null) delete this[name]; else this[name] = x; + } + return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; + } + d3_selectionPrototype.text = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.textContent = v == null ? "" : v; + } : value == null ? function() { + this.textContent = ""; + } : function() { + this.textContent = value; + }) : this.node().textContent; + }; + d3_selectionPrototype.html = function(value) { + return arguments.length ? this.each(typeof value === "function" ? function() { + var v = value.apply(this, arguments); + this.innerHTML = v == null ? "" : v; + } : value == null ? function() { + this.innerHTML = ""; + } : function() { + this.innerHTML = value; + }) : this.node().innerHTML; + }; + d3_selectionPrototype.append = function(name) { + name = d3.ns.qualify(name); + function append() { + return this.appendChild(d3_document.createElementNS(this.namespaceURI, name)); + } + function appendNS() { + return this.appendChild(d3_document.createElementNS(name.space, name.local)); + } + return this.select(name.local ? appendNS : append); + }; + d3_selectionPrototype.insert = function(name, before) { + name = d3.ns.qualify(name); + function insert() { + return this.insertBefore(d3_document.createElementNS(this.namespaceURI, name), d3_select(before, this)); + } + function insertNS() { + return this.insertBefore(d3_document.createElementNS(name.space, name.local), d3_select(before, this)); + } + return this.select(name.local ? insertNS : insert); + }; + d3_selectionPrototype.remove = function() { + return this.each(function() { + var parent = this.parentNode; + if (parent) parent.removeChild(this); + }); + }; + d3_selectionPrototype.data = function(value, key) { + var i = -1, n = this.length, group, node; + if (!arguments.length) { + value = new Array(n = (group = this[0]).length); + while (++i < n) { + if (node = group[i]) { + value[i] = node.__data__; + } + } + return value; + } + function bind(group, groupData) { + var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; + if (key) { + var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue; + for (i = -1; ++i < n; ) { + keyValue = key.call(node = group[i], node.__data__, i); + if (nodeByKeyValue.has(keyValue)) { + exitNodes[i] = node; + } else { + nodeByKeyValue.set(keyValue, node); + } + keyValues.push(keyValue); + } + for (i = -1; ++i < m; ) { + keyValue = key.call(groupData, nodeData = groupData[i], i); + if (node = nodeByKeyValue.get(keyValue)) { + updateNodes[i] = node; + node.__data__ = nodeData; + } else if (!dataByKeyValue.has(keyValue)) { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + dataByKeyValue.set(keyValue, nodeData); + nodeByKeyValue.remove(keyValue); + } + for (i = -1; ++i < n; ) { + if (nodeByKeyValue.has(keyValues[i])) { + exitNodes[i] = group[i]; + } + } + } else { + for (i = -1; ++i < n0; ) { + node = group[i]; + nodeData = groupData[i]; + if (node) { + node.__data__ = nodeData; + updateNodes[i] = node; + } else { + enterNodes[i] = d3_selection_dataNode(nodeData); + } + } + for (;i < m; ++i) { + enterNodes[i] = d3_selection_dataNode(groupData[i]); + } + for (;i < n; ++i) { + exitNodes[i] = group[i]; + } + } + enterNodes.update = updateNodes; + enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; + enter.push(enterNodes); + update.push(updateNodes); + exit.push(exitNodes); + } + var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); + if (typeof value === "function") { + while (++i < n) { + bind(group = this[i], value.call(group, group.parentNode.__data__, i)); + } + } else { + while (++i < n) { + bind(group = this[i], value); + } + } + update.enter = function() { + return enter; + }; + update.exit = function() { + return exit; + }; + return update; + }; + function d3_selection_dataNode(data) { + return { + __data__: data + }; + } + d3_selectionPrototype.datum = function(value) { + return arguments.length ? this.property("__data__", value) : this.property("__data__"); + }; + d3_selectionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + subgroup.parentNode = (group = this[j]).parentNode; + for (var i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i)) { + subgroup.push(node); + } + } + } + return d3_selection(subgroups); + }; + function d3_selection_filter(selector) { + return function() { + return d3_selectMatches(this, selector); + }; + } + d3_selectionPrototype.order = function() { + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { + if (node = group[i]) { + if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); + next = node; + } + } + } + return this; + }; + d3_selectionPrototype.sort = function(comparator) { + comparator = d3_selection_sortComparator.apply(this, arguments); + for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); + return this.order(); + }; + function d3_selection_sortComparator(comparator) { + if (!arguments.length) comparator = d3.ascending; + return function(a, b) { + return !a - !b || comparator(a.__data__, b.__data__); + }; + } + d3_selectionPrototype.on = function(type, listener, capture) { + var n = arguments.length; + if (n < 3) { + if (typeof type !== "string") { + if (n < 2) listener = false; + for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); + return this; + } + if (n < 2) return (n = this.node()["__on" + type]) && n._; + capture = false; + } + return this.each(d3_selection_on(type, listener, capture)); + }; + function d3_selection_on(type, listener, capture) { + var name = "__on" + type, i = type.indexOf("."); + if (i > 0) type = type.substring(0, i); + function onRemove() { + var wrapper = this[name]; + if (wrapper) { + this.removeEventListener(type, wrapper, wrapper.$); + delete this[name]; + } + } + function onAdd() { + var node = this, args = d3_array(arguments); + onRemove.call(this); + this.addEventListener(type, this[name] = wrapper, wrapper.$ = capture); + wrapper._ = listener; + function wrapper(e) { + var o = d3.event; + d3.event = e; + args[0] = node.__data__; + try { + listener.apply(node, args); + } finally { + d3.event = o; + } + } + } + return listener ? onAdd : onRemove; + } + d3_selectionPrototype.each = function(callback) { + return d3_selection_each(this, function(node, i, j) { + callback.call(node, node.__data__, i, j); + }); + }; + function d3_selection_each(groups, callback) { + for (var j = 0, m = groups.length; j < m; j++) { + for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { + if (node = group[i]) callback(node, i, j); + } + } + return groups; + } + d3_selectionPrototype.call = function(callback) { + var args = d3_array(arguments); + callback.apply(args[0] = this, args); + return this; + }; + d3_selectionPrototype.empty = function() { + return !this.node(); + }; + d3_selectionPrototype.node = function() { + for (var j = 0, m = this.length; j < m; j++) { + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + var node = group[i]; + if (node) return node; + } + } + return null; + }; + d3_selectionPrototype.transition = function() { + var id = d3_transitionInheritId || ++d3_transitionId, subgroups = [], subgroup, node, transition = Object.create(d3_transitionInherit); + transition.time = Date.now(); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) d3_transitionNode(node, i, id, transition); + subgroup.push(node); + } + } + return d3_transition(subgroups, id); + }; + var d3_selectionRoot = d3_selection([ [ d3_document ] ]); + d3_selectionRoot[0].parentNode = d3_selectRoot; + d3.select = function(selector) { + return typeof selector === "string" ? d3_selectionRoot.select(selector) : d3_selection([ [ selector ] ]); + }; + d3.selectAll = function(selector) { + return typeof selector === "string" ? d3_selectionRoot.selectAll(selector) : d3_selection([ d3_array(selector) ]); + }; + function d3_selection_enter(selection) { + d3_arraySubclass(selection, d3_selection_enterPrototype); + return selection; + } + var d3_selection_enterPrototype = []; + d3.selection.enter = d3_selection_enter; + d3.selection.enter.prototype = d3_selection_enterPrototype; + d3_selection_enterPrototype.append = d3_selectionPrototype.append; + d3_selection_enterPrototype.insert = d3_selectionPrototype.insert; + d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; + d3_selection_enterPrototype.node = d3_selectionPrototype.node; + d3_selection_enterPrototype.select = function(selector) { + var subgroups = [], subgroup, subnode, upgroup, group, node; + for (var j = -1, m = this.length; ++j < m; ) { + upgroup = (group = this[j]).update; + subgroups.push(subgroup = []); + subgroup.parentNode = group.parentNode; + for (var i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i)); + subnode.__data__ = node.__data__; + } else { + subgroup.push(null); + } + } + } + return d3_selection(subgroups); + }; + function d3_transition(groups, id) { + d3_arraySubclass(groups, d3_transitionPrototype); + groups.id = id; + return groups; + } + var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit = { + ease: d3_ease_cubicInOut, + delay: 0, + duration: 250 + }; + d3_transitionPrototype.call = d3_selectionPrototype.call; + d3_transitionPrototype.empty = d3_selectionPrototype.empty; + d3_transitionPrototype.node = d3_selectionPrototype.node; + d3.transition = function(selection) { + return arguments.length ? d3_transitionInheritId ? selection.transition() : selection : d3_selectionRoot.transition(); + }; + d3.transition.prototype = d3_transitionPrototype; + function d3_transitionNode(node, i, id, inherit) { + var lock = node.__transition__ || (node.__transition__ = { + active: 0, + count: 0 + }), transition = lock[id]; + if (!transition) { + var time = inherit.time; + transition = lock[id] = { + tween: new d3_Map(), + event: d3.dispatch("start", "end"), + time: time, + ease: inherit.ease, + delay: inherit.delay, + duration: inherit.duration + }; + ++lock.count; + d3.timer(function(elapsed) { + var d = node.__data__, ease = transition.ease, event = transition.event, delay = transition.delay, duration = transition.duration, tweened = []; + return delay <= elapsed ? start(elapsed) : d3.timer(start, delay, time), 1; + function start(elapsed) { + if (lock.active > id) return stop(); + lock.active = id; + event.start.call(node, d, i); + transition.tween.forEach(function(key, value) { + if (value = value.call(node, d, i)) { + tweened.push(value); + } + }); + if (!tick(elapsed)) d3.timer(tick, 0, time); + return 1; + } + function tick(elapsed) { + if (lock.active !== id) return stop(); + var t = (elapsed - delay) / duration, e = ease(t), n = tweened.length; + while (n > 0) { + tweened[--n].call(node, e); + } + if (t >= 1) { + stop(); + event.end.call(node, d, i); + return 1; + } + } + function stop() { + if (--lock.count) delete lock[id]; else delete node.__transition__; + return 1; + } + }, 0, time); + return transition; + } + } + d3_transitionPrototype.select = function(selector) { + var id = this.id, subgroups = [], subgroup, subnode, node; + if (typeof selector !== "function") selector = d3_selection_selector(selector); + for (var j = -1, m = this.length; ++j < m; ) { + subgroups.push(subgroup = []); + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i))) { + if ("__data__" in node) subnode.__data__ = node.__data__; + d3_transitionNode(subnode, i, id, node.__transition__[id]); + subgroup.push(subnode); + } else { + subgroup.push(null); + } + } + } + return d3_transition(subgroups, id); + }; + d3_transitionPrototype.selectAll = function(selector) { + var id = this.id, subgroups = [], subgroup, subnodes, node, subnode, transition; + if (typeof selector !== "function") selector = d3_selection_selectorAll(selector); + for (var j = -1, m = this.length; ++j < m; ) { + for (var group = this[j], i = -1, n = group.length; ++i < n; ) { + if (node = group[i]) { + transition = node.__transition__[id]; + subnodes = selector.call(node, node.__data__, i); + subgroups.push(subgroup = []); + for (var k = -1, o = subnodes.length; ++k < o; ) { + d3_transitionNode(subnode = subnodes[k], k, id, transition); + subgroup.push(subnode); + } + } + } + } + return d3_transition(subgroups, id); + }; + d3_transitionPrototype.filter = function(filter) { + var subgroups = [], subgroup, group, node; + if (typeof filter !== "function") filter = d3_selection_filter(filter); + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if ((node = group[i]) && filter.call(node, node.__data__, i)) { + subgroup.push(node); + } + } + } + return d3_transition(subgroups, this.id, this.time).ease(this.ease()); + }; + d3_transitionPrototype.attr = function(nameNS, value) { + if (arguments.length < 2) { + for (value in nameNS) this.attr(value, nameNS[value]); + return this; + } + var interpolate = d3_interpolateByName(nameNS), name = d3.ns.qualify(nameNS); + function attrNull() { + this.removeAttribute(name); + } + function attrNullNS() { + this.removeAttributeNS(name.space, name.local); + } + return d3_transition_tween(this, "attr." + nameNS, value, function(b) { + function attrString() { + var a = this.getAttribute(name), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttribute(name, i(t)); + }); + } + function attrStringNS() { + var a = this.getAttributeNS(name.space, name.local), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.setAttributeNS(name.space, name.local, i(t)); + }); + } + return b == null ? name.local ? attrNullNS : attrNull : (b += "", name.local ? attrStringNS : attrString); + }); + }; + d3_transitionPrototype.attrTween = function(nameNS, tween) { + var name = d3.ns.qualify(nameNS); + function attrTween(d, i) { + var f = tween.call(this, d, i, this.getAttribute(name)); + return f && function(t) { + this.setAttribute(name, f(t)); + }; + } + function attrTweenNS(d, i) { + var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); + return f && function(t) { + this.setAttributeNS(name.space, name.local, f(t)); + }; + } + return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); + }; + d3_transitionPrototype.style = function(name, value, priority) { + var n = arguments.length; + if (n < 3) { + if (typeof name !== "string") { + if (n < 2) value = ""; + for (priority in name) this.style(priority, name[priority], value); + return this; + } + priority = ""; + } + var interpolate = d3_interpolateByName(name); + function styleNull() { + this.style.removeProperty(name); + } + return d3_transition_tween(this, "style." + name, value, function(b) { + function styleString() { + var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i; + return a !== b && (i = interpolate(a, b), function(t) { + this.style.setProperty(name, i(t), priority); + }); + } + return b == null ? styleNull : (b += "", styleString); + }); + }; + d3_transitionPrototype.styleTween = function(name, tween, priority) { + if (arguments.length < 3) priority = ""; + return this.tween("style." + name, function(d, i) { + var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name)); + return f && function(t) { + this.style.setProperty(name, f(t), priority); + }; + }); + }; + d3_transitionPrototype.text = function(value) { + return d3_transition_tween(this, "text", value, d3_transition_text); + }; + function d3_transition_text(b) { + if (b == null) b = ""; + return function() { + this.textContent = b; + }; + } + d3_transitionPrototype.remove = function() { + return this.each("end.transition", function() { + var p; + if (!this.__transition__ && (p = this.parentNode)) p.removeChild(this); + }); + }; + d3_transitionPrototype.ease = function(value) { + var id = this.id; + if (arguments.length < 1) return this.node().__transition__[id].ease; + if (typeof value !== "function") value = d3.ease.apply(d3, arguments); + return d3_selection_each(this, function(node) { + node.__transition__[id].ease = value; + }); + }; + d3_transitionPrototype.delay = function(value) { + var id = this.id; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node.__transition__[id].delay = value.call(node, node.__data__, i, j) | 0; + } : (value |= 0, function(node) { + node.__transition__[id].delay = value; + })); + }; + d3_transitionPrototype.duration = function(value) { + var id = this.id; + return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { + node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j) | 0); + } : (value = Math.max(1, value | 0), function(node) { + node.__transition__[id].duration = value; + })); + }; + d3_transitionPrototype.each = function(type, listener) { + var id = this.id; + if (arguments.length < 2) { + var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; + d3_transitionInheritId = id; + d3_selection_each(this, function(node, i, j) { + d3_transitionInherit = node.__transition__[id]; + type.call(node, node.__data__, i, j); + }); + d3_transitionInherit = inherit; + d3_transitionInheritId = inheritId; + } else { + d3_selection_each(this, function(node) { + node.__transition__[id].event.on(type, listener); + }); + } + return this; + }; + d3_transitionPrototype.transition = function() { + var id0 = this.id, id1 = ++d3_transitionId, subgroups = [], subgroup, group, node, transition; + for (var j = 0, m = this.length; j < m; j++) { + subgroups.push(subgroup = []); + for (var group = this[j], i = 0, n = group.length; i < n; i++) { + if (node = group[i]) { + transition = Object.create(node.__transition__[id0]); + transition.delay += transition.duration; + d3_transitionNode(node, i, id1, transition); + } + subgroup.push(node); + } + } + return d3_transition(subgroups, id1); + }; + d3_transitionPrototype.tween = function(name, tween) { + var id = this.id; + if (arguments.length < 2) return this.node().__transition__[id].tween.get(name); + return d3_selection_each(this, tween == null ? function(node) { + node.__transition__[id].tween.remove(name); + } : function(node) { + node.__transition__[id].tween.set(name, tween); + }); + }; + function d3_transition_tween(groups, name, value, tween) { + var id = groups.id; + return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { + node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j))); + } : (value = tween(value), function(node) { + node.__transition__[id].tween.set(name, value); + })); + } + var d3_timer_id = 0, d3_timer_byId = {}, d3_timer_queue = null, d3_timer_interval, d3_timer_timeout; + d3.timer = function(callback, delay, then) { + if (arguments.length < 3) { + if (arguments.length < 2) delay = 0; else if (!isFinite(delay)) return; + then = Date.now(); + } + var timer = d3_timer_byId[callback.id]; + if (timer && timer.callback === callback) { + timer.then = then; + timer.delay = delay; + } else d3_timer_byId[callback.id = ++d3_timer_id] = d3_timer_queue = { + callback: callback, + then: then, + delay: delay, + next: d3_timer_queue + }; + if (!d3_timer_interval) { + d3_timer_timeout = clearTimeout(d3_timer_timeout); + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + }; + function d3_timer_step() { + var elapsed, now = Date.now(), t1 = d3_timer_queue; + while (t1) { + elapsed = now - t1.then; + if (elapsed >= t1.delay) t1.flush = t1.callback(elapsed); + t1 = t1.next; + } + var delay = d3_timer_flush() - now; + if (delay > 24) { + if (isFinite(delay)) { + clearTimeout(d3_timer_timeout); + d3_timer_timeout = setTimeout(d3_timer_step, delay); + } + d3_timer_interval = 0; + } else { + d3_timer_interval = 1; + d3_timer_frame(d3_timer_step); + } + } + d3.timer.flush = function() { + var elapsed, now = Date.now(), t1 = d3_timer_queue; + while (t1) { + elapsed = now - t1.then; + if (!t1.delay) t1.flush = t1.callback(elapsed); + t1 = t1.next; + } + d3_timer_flush(); + }; + function d3_timer_flush() { + var t0 = null, t1 = d3_timer_queue, then = Infinity; + while (t1) { + if (t1.flush) { + delete d3_timer_byId[t1.callback.id]; + t1 = t0 ? t0.next = t1.next : d3_timer_queue = t1.next; + } else { + then = Math.min(then, t1.then + t1.delay); + t1 = (t0 = t1).next; + } + } + return then; + } + var d3_timer_frame = d3_window.requestAnimationFrame || d3_window.webkitRequestAnimationFrame || d3_window.mozRequestAnimationFrame || d3_window.oRequestAnimationFrame || d3_window.msRequestAnimationFrame || function(callback) { + setTimeout(callback, 17); + }; + d3.mouse = function(container) { + return d3_mousePoint(container, d3_eventSource()); + }; + var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0; + function d3_mousePoint(container, e) { + var svg = container.ownerSVGElement || container; + if (svg.createSVGPoint) { + var point = svg.createSVGPoint(); + if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) { + svg = d3.select(d3_document.body).append("svg").style("position", "absolute").style("top", 0).style("left", 0); + var ctm = svg[0][0].getScreenCTM(); + d3_mouse_bug44083 = !(ctm.f || ctm.e); + svg.remove(); + } + if (d3_mouse_bug44083) { + point.x = e.pageX; + point.y = e.pageY; + } else { + point.x = e.clientX; + point.y = e.clientY; + } + point = point.matrixTransform(container.getScreenCTM().inverse()); + return [ point.x, point.y ]; + } + var rect = container.getBoundingClientRect(); + return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; + } + d3.touches = function(container, touches) { + if (arguments.length < 2) touches = d3_eventSource().touches; + return touches ? d3_array(touches).map(function(touch) { + var point = d3_mousePoint(container, touch); + point.identifier = touch.identifier; + return point; + }) : []; + }; + function d3_noop() {} + d3.scale = {}; + function d3_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_scaleRange(scale) { + return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); + } + function d3_scale_nice(domain, nice) { + var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; + if (x1 < x0) { + dx = i0, i0 = i1, i1 = dx; + dx = x0, x0 = x1, x1 = dx; + } + if (nice = nice(x1 - x0)) { + domain[i0] = nice.floor(x0); + domain[i1] = nice.ceil(x1); + } + return domain; + } + function d3_scale_niceDefault() { + return Math; + } + d3.scale.linear = function() { + return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3.interpolate, false); + }; + function d3_scale_linear(domain, range, interpolate, clamp) { + var output, input; + function rescale() { + var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; + output = linear(domain, range, uninterpolate, interpolate); + input = linear(range, domain, uninterpolate, d3.interpolate); + return scale; + } + function scale(x) { + return output(x); + } + scale.invert = function(y) { + return input(y); + }; + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.map(Number); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.rangeRound = function(x) { + return scale.range(x).interpolate(d3.interpolateRound); + }; + scale.clamp = function(x) { + if (!arguments.length) return clamp; + clamp = x; + return rescale(); + }; + scale.interpolate = function(x) { + if (!arguments.length) return interpolate; + interpolate = x; + return rescale(); + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + scale.tickFormat = function(m) { + return d3_scale_linearTickFormat(domain, m); + }; + scale.nice = function() { + d3_scale_nice(domain, d3_scale_linearNice); + return rescale(); + }; + scale.copy = function() { + return d3_scale_linear(domain, range, interpolate, clamp); + }; + return rescale(); + } + function d3_scale_linearRebind(scale, linear) { + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_scale_linearNice(dx) { + dx = Math.pow(10, Math.round(Math.log(dx) / Math.LN10) - 1); + return dx && { + floor: function(x) { + return Math.floor(x / dx) * dx; + }, + ceil: function(x) { + return Math.ceil(x / dx) * dx; + } + }; + } + function d3_scale_linearTickRange(domain, m) { + var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; + if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; + extent[0] = Math.ceil(extent[0] / step) * step; + extent[1] = Math.floor(extent[1] / step) * step + step * .5; + extent[2] = step; + return extent; + } + function d3_scale_linearTicks(domain, m) { + return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); + } + function d3_scale_linearTickFormat(domain, m) { + return d3.format(",." + Math.max(0, -Math.floor(Math.log(d3_scale_linearTickRange(domain, m)[2]) / Math.LN10 + .01)) + "f"); + } + function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { + var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); + return function(x) { + return i(u(x)); + }; + } + function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { + var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; + if (domain[k] < domain[0]) { + domain = domain.slice().reverse(); + range = range.slice().reverse(); + } + while (++j <= k) { + u.push(uninterpolate(domain[j - 1], domain[j])); + i.push(interpolate(range[j - 1], range[j])); + } + return function(x) { + var j = d3.bisect(domain, x, 1, k) - 1; + return i[j](u[j](x)); + }; + } + d3.scale.log = function() { + return d3_scale_log(d3.scale.linear(), d3_scale_logp); + }; + function d3_scale_log(linear, log) { + var pow = log.pow; + function scale(x) { + return linear(log(x)); + } + scale.invert = function(x) { + return pow(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(pow); + log = x[0] < 0 ? d3_scale_logn : d3_scale_logp; + pow = log.pow; + linear.domain(x.map(log)); + return scale; + }; + scale.nice = function() { + linear.domain(d3_scale_nice(linear.domain(), d3_scale_niceDefault)); + return scale; + }; + scale.ticks = function() { + var extent = d3_scaleExtent(linear.domain()), ticks = []; + if (extent.every(isFinite)) { + var i = Math.floor(extent[0]), j = Math.ceil(extent[1]), u = pow(extent[0]), v = pow(extent[1]); + if (log === d3_scale_logn) { + ticks.push(pow(i)); + for (;i++ < j; ) for (var k = 9; k > 0; k--) ticks.push(pow(i) * k); + } else { + for (;i < j; i++) for (var k = 1; k < 10; k++) ticks.push(pow(i) * k); + ticks.push(pow(i)); + } + for (i = 0; ticks[i] < u; i++) {} + for (j = ticks.length; ticks[j - 1] > v; j--) {} + ticks = ticks.slice(i, j); + } + return ticks; + }; + scale.tickFormat = function(n, format) { + if (arguments.length < 2) format = d3_scale_logFormat; + if (!arguments.length) return format; + var k = Math.max(.1, n / scale.ticks().length), f = log === d3_scale_logn ? (e = -1e-12, + Math.floor) : (e = 1e-12, Math.ceil), e; + return function(d) { + return d / pow(f(log(d) + e)) <= k ? format(d) : ""; + }; + }; + scale.copy = function() { + return d3_scale_log(linear.copy(), log); + }; + return d3_scale_linearRebind(scale, linear); + } + var d3_scale_logFormat = d3.format(".0e"); + function d3_scale_logp(x) { + return Math.log(x < 0 ? 0 : x) / Math.LN10; + } + function d3_scale_logn(x) { + return -Math.log(x > 0 ? 0 : -x) / Math.LN10; + } + d3_scale_logp.pow = function(x) { + return Math.pow(10, x); + }; + d3_scale_logn.pow = function(x) { + return -Math.pow(10, -x); + }; + d3.scale.pow = function() { + return d3_scale_pow(d3.scale.linear(), 1); + }; + function d3_scale_pow(linear, exponent) { + var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); + function scale(x) { + return linear(powp(x)); + } + scale.invert = function(x) { + return powb(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(powb); + linear.domain(x.map(powp)); + return scale; + }; + scale.ticks = function(m) { + return d3_scale_linearTicks(scale.domain(), m); + }; + scale.tickFormat = function(m) { + return d3_scale_linearTickFormat(scale.domain(), m); + }; + scale.nice = function() { + return scale.domain(d3_scale_nice(scale.domain(), d3_scale_linearNice)); + }; + scale.exponent = function(x) { + if (!arguments.length) return exponent; + var domain = scale.domain(); + powp = d3_scale_powPow(exponent = x); + powb = d3_scale_powPow(1 / exponent); + return scale.domain(domain); + }; + scale.copy = function() { + return d3_scale_pow(linear.copy(), exponent); + }; + return d3_scale_linearRebind(scale, linear); + } + function d3_scale_powPow(e) { + return function(x) { + return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); + }; + } + d3.scale.sqrt = function() { + return d3.scale.pow().exponent(.5); + }; + d3.scale.ordinal = function() { + return d3_scale_ordinal([], { + t: "range", + a: [ [] ] + }); + }; + function d3_scale_ordinal(domain, ranger) { + var index, range, rangeBand; + function scale(x) { + return range[((index.get(x) || index.set(x, domain.push(x))) - 1) % range.length]; + } + function steps(start, step) { + return d3.range(domain.length).map(function(i) { + return start + step * i; + }); + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = []; + index = new d3_Map(); + var i = -1, n = x.length, xi; + while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); + return scale[ranger.t].apply(scale, ranger.a); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + rangeBand = 0; + ranger = { + t: "range", + a: arguments + }; + return scale; + }; + scale.rangePoints = function(x, padding) { + if (arguments.length < 2) padding = 0; + var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding); + range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step); + rangeBand = 0; + ranger = { + t: "rangePoints", + a: arguments + }; + return scale; + }; + scale.rangeBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); + range = steps(start + step * outerPadding, step); + if (reverse) range.reverse(); + rangeBand = step * (1 - padding); + ranger = { + t: "rangeBands", + a: arguments + }; + return scale; + }; + scale.rangeRoundBands = function(x, padding, outerPadding) { + if (arguments.length < 2) padding = 0; + if (arguments.length < 3) outerPadding = padding; + var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step; + range = steps(start + Math.round(error / 2), step); + if (reverse) range.reverse(); + rangeBand = Math.round(step * (1 - padding)); + ranger = { + t: "rangeRoundBands", + a: arguments + }; + return scale; + }; + scale.rangeBand = function() { + return rangeBand; + }; + scale.rangeExtent = function() { + return d3_scaleExtent(ranger.a[0]); + }; + scale.copy = function() { + return d3_scale_ordinal(domain, ranger); + }; + return scale.domain(domain); + } + d3.scale.category10 = function() { + return d3.scale.ordinal().range(d3_category10); + }; + d3.scale.category20 = function() { + return d3.scale.ordinal().range(d3_category20); + }; + d3.scale.category20b = function() { + return d3.scale.ordinal().range(d3_category20b); + }; + d3.scale.category20c = function() { + return d3.scale.ordinal().range(d3_category20c); + }; + var d3_category10 = [ "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf" ]; + var d3_category20 = [ "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c", "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5", "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f", "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5" ]; + var d3_category20b = [ "#393b79", "#5254a3", "#6b6ecf", "#9c9ede", "#637939", "#8ca252", "#b5cf6b", "#cedb9c", "#8c6d31", "#bd9e39", "#e7ba52", "#e7cb94", "#843c39", "#ad494a", "#d6616b", "#e7969c", "#7b4173", "#a55194", "#ce6dbd", "#de9ed6" ]; + var d3_category20c = [ "#3182bd", "#6baed6", "#9ecae1", "#c6dbef", "#e6550d", "#fd8d3c", "#fdae6b", "#fdd0a2", "#31a354", "#74c476", "#a1d99b", "#c7e9c0", "#756bb1", "#9e9ac8", "#bcbddc", "#dadaeb", "#636363", "#969696", "#bdbdbd", "#d9d9d9" ]; + d3.scale.quantile = function() { + return d3_scale_quantile([], []); + }; + function d3_scale_quantile(domain, range) { + var thresholds; + function rescale() { + var k = 0, q = range.length; + thresholds = []; + while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); + return scale; + } + function scale(x) { + if (isNaN(x = +x)) return NaN; + return range[d3.bisect(thresholds, x)]; + } + scale.domain = function(x) { + if (!arguments.length) return domain; + domain = x.filter(function(d) { + return !isNaN(d); + }).sort(d3.ascending); + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.quantiles = function() { + return thresholds; + }; + scale.copy = function() { + return d3_scale_quantile(domain, range); + }; + return rescale(); + } + d3.scale.quantize = function() { + return d3_scale_quantize(0, 1, [ 0, 1 ]); + }; + function d3_scale_quantize(x0, x1, range) { + var kx, i; + function scale(x) { + return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; + } + function rescale() { + kx = range.length / (x1 - x0); + i = range.length - 1; + return scale; + } + scale.domain = function(x) { + if (!arguments.length) return [ x0, x1 ]; + x0 = +x[0]; + x1 = +x[x.length - 1]; + return rescale(); + }; + scale.range = function(x) { + if (!arguments.length) return range; + range = x; + return rescale(); + }; + scale.copy = function() { + return d3_scale_quantize(x0, x1, range); + }; + return rescale(); + } + d3.scale.threshold = function() { + return d3_scale_threshold([ .5 ], [ 0, 1 ]); + }; + function d3_scale_threshold(domain, range) { + function scale(x) { + return range[d3.bisect(domain, x)]; + } + scale.domain = function(_) { + if (!arguments.length) return domain; + domain = _; + return scale; + }; + scale.range = function(_) { + if (!arguments.length) return range; + range = _; + return scale; + }; + scale.copy = function() { + return d3_scale_threshold(domain, range); + }; + return scale; + } + d3.scale.identity = function() { + return d3_scale_identity([ 0, 1 ]); + }; + function d3_scale_identity(domain) { + function identity(x) { + return +x; + } + identity.invert = identity; + identity.domain = identity.range = function(x) { + if (!arguments.length) return domain; + domain = x.map(identity); + return identity; + }; + identity.ticks = function(m) { + return d3_scale_linearTicks(domain, m); + }; + identity.tickFormat = function(m) { + return d3_scale_linearTickFormat(domain, m); + }; + identity.copy = function() { + return d3_scale_identity(domain); + }; + return identity; + } + d3.svg = {}; + d3.svg.arc = function() { + var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function arc() { + var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0, + a0 = a1, a1 = da), a1 - a0), df = da < π ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1); + return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z"; + } + arc.innerRadius = function(v) { + if (!arguments.length) return innerRadius; + innerRadius = d3_functor(v); + return arc; + }; + arc.outerRadius = function(v) { + if (!arguments.length) return outerRadius; + outerRadius = d3_functor(v); + return arc; + }; + arc.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return arc; + }; + arc.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return arc; + }; + arc.centroid = function() { + var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset; + return [ Math.cos(a) * r, Math.sin(a) * r ]; + }; + return arc; + }; + var d3_svg_arcOffset = -π / 2, d3_svg_arcMax = 2 * π - 1e-6; + function d3_svg_arcInnerRadius(d) { + return d.innerRadius; + } + function d3_svg_arcOuterRadius(d) { + return d.outerRadius; + } + function d3_svg_arcStartAngle(d) { + return d.startAngle; + } + function d3_svg_arcEndAngle(d) { + return d.endAngle; + } + function d3_svg_line(projection) { + var x = d3_svg_lineX, y = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; + function line(data) { + var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); + function segment() { + segments.push("M", interpolate(projection(points), tension)); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); + } else if (points.length) { + segment(); + points = []; + } + } + if (points.length) segment(); + return segments.length ? segments.join("") : null; + } + line.x = function(_) { + if (!arguments.length) return x; + x = _; + return line; + }; + line.y = function(_) { + if (!arguments.length) return y; + y = _; + return line; + }; + line.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return line; + }; + line.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + return line; + }; + line.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return line; + }; + return line; + } + d3.svg.line = function() { + return d3_svg_line(d3_identity); + }; + function d3_svg_lineX(d) { + return d[0]; + } + function d3_svg_lineY(d) { + return d[1]; + } + var d3_svg_lineInterpolators = d3.map({ + linear: d3_svg_lineLinear, + "linear-closed": d3_svg_lineLinearClosed, + "step-before": d3_svg_lineStepBefore, + "step-after": d3_svg_lineStepAfter, + basis: d3_svg_lineBasis, + "basis-open": d3_svg_lineBasisOpen, + "basis-closed": d3_svg_lineBasisClosed, + bundle: d3_svg_lineBundle, + cardinal: d3_svg_lineCardinal, + "cardinal-open": d3_svg_lineCardinalOpen, + "cardinal-closed": d3_svg_lineCardinalClosed, + monotone: d3_svg_lineMonotone + }); + d3_svg_lineInterpolators.forEach(function(key, value) { + value.key = key; + value.closed = /-closed$/.test(key); + }); + function d3_svg_lineLinear(points) { + return points.join("L"); + } + function d3_svg_lineLinearClosed(points) { + return d3_svg_lineLinear(points) + "Z"; + } + function d3_svg_lineStepBefore(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); + return path.join(""); + } + function d3_svg_lineStepAfter(points) { + var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; + while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); + return path.join(""); + } + function d3_svg_lineCardinalOpen(points, tension) { + return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineCardinalClosed(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), + points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); + } + function d3_svg_lineCardinal(points, tension) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); + } + function d3_svg_lineHermite(points, tangents) { + if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { + return d3_svg_lineLinear(points); + } + var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; + if (quad) { + path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; + p0 = points[1]; + pi = 2; + } + if (tangents.length > 1) { + t = tangents[1]; + p = points[pi]; + pi++; + path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + for (var i = 2; i < tangents.length; i++, pi++) { + p = points[pi]; + t = tangents[i]; + path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; + } + } + if (quad) { + var lp = points[pi]; + path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; + } + return path; + } + function d3_svg_lineCardinalTangents(points, tension) { + var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; + while (++i < n) { + p0 = p1; + p1 = p2; + p2 = points[i]; + tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); + } + return tangents; + } + function d3_svg_lineBasis(points) { + if (points.length < 3) return d3_svg_lineLinear(points); + var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0 ]; + d3_svg_lineBasisBezier(path, px, py); + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + i = -1; + while (++i < 2) { + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisOpen(points) { + if (points.length < 4) return d3_svg_lineLinear(points); + var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; + while (++i < 3) { + pi = points[i]; + px.push(pi[0]); + py.push(pi[1]); + } + path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); + --i; + while (++i < n) { + pi = points[i]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBasisClosed(points) { + var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; + while (++i < 4) { + pi = points[i % n]; + px.push(pi[0]); + py.push(pi[1]); + } + path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; + --i; + while (++i < m) { + pi = points[i % n]; + px.shift(); + px.push(pi[0]); + py.shift(); + py.push(pi[1]); + d3_svg_lineBasisBezier(path, px, py); + } + return path.join(""); + } + function d3_svg_lineBundle(points, tension) { + var n = points.length - 1; + if (n) { + var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; + while (++i <= n) { + p = points[i]; + t = i / n; + p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); + p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); + } + } + return d3_svg_lineBasis(points); + } + function d3_svg_lineDot4(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; + } + var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; + function d3_svg_lineBasisBezier(path, x, y) { + path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); + } + function d3_svg_lineSlope(p0, p1) { + return (p1[1] - p0[1]) / (p1[0] - p0[0]); + } + function d3_svg_lineFiniteDifferences(points) { + var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); + while (++i < j) { + m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; + } + m[i] = d; + return m; + } + function d3_svg_lineMonotoneTangents(points) { + var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; + while (++i < j) { + d = d3_svg_lineSlope(points[i], points[i + 1]); + if (Math.abs(d) < 1e-6) { + m[i] = m[i + 1] = 0; + } else { + a = m[i] / d; + b = m[i + 1] / d; + s = a * a + b * b; + if (s > 9) { + s = d * 3 / Math.sqrt(s); + m[i] = s * a; + m[i + 1] = s * b; + } + } + } + i = -1; + while (++i <= j) { + s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); + tangents.push([ s || 0, m[i] * s || 0 ]); + } + return tangents; + } + function d3_svg_lineMonotone(points) { + return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); + } + d3.svg.line.radial = function() { + var line = d3_svg_line(d3_svg_lineRadial); + line.radius = line.x, delete line.x; + line.angle = line.y, delete line.y; + return line; + }; + function d3_svg_lineRadial(points) { + var point, i = -1, n = points.length, r, a; + while (++i < n) { + point = points[i]; + r = point[0]; + a = point[1] + d3_svg_arcOffset; + point[0] = r * Math.cos(a); + point[1] = r * Math.sin(a); + } + return points; + } + function d3_svg_area(projection) { + var x0 = d3_svg_lineX, x1 = d3_svg_lineX, y0 = 0, y1 = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; + function area(data) { + var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { + return x; + } : d3_functor(x1), fy1 = y0 === y1 ? function() { + return y; + } : d3_functor(y1), x, y; + function segment() { + segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); + } + while (++i < n) { + if (defined.call(this, d = data[i], i)) { + points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); + points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); + } else if (points0.length) { + segment(); + points0 = []; + points1 = []; + } + } + if (points0.length) segment(); + return segments.length ? segments.join("") : null; + } + area.x = function(_) { + if (!arguments.length) return x1; + x0 = x1 = _; + return area; + }; + area.x0 = function(_) { + if (!arguments.length) return x0; + x0 = _; + return area; + }; + area.x1 = function(_) { + if (!arguments.length) return x1; + x1 = _; + return area; + }; + area.y = function(_) { + if (!arguments.length) return y1; + y0 = y1 = _; + return area; + }; + area.y0 = function(_) { + if (!arguments.length) return y0; + y0 = _; + return area; + }; + area.y1 = function(_) { + if (!arguments.length) return y1; + y1 = _; + return area; + }; + area.defined = function(_) { + if (!arguments.length) return defined; + defined = _; + return area; + }; + area.interpolate = function(_) { + if (!arguments.length) return interpolateKey; + if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; + interpolateReverse = interpolate.reverse || interpolate; + L = interpolate.closed ? "M" : "L"; + return area; + }; + area.tension = function(_) { + if (!arguments.length) return tension; + tension = _; + return area; + }; + return area; + } + d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; + d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; + d3.svg.area = function() { + return d3_svg_area(d3_identity); + }; + d3.svg.area.radial = function() { + var area = d3_svg_area(d3_svg_lineRadial); + area.radius = area.x, delete area.x; + area.innerRadius = area.x0, delete area.x0; + area.outerRadius = area.x1, delete area.x1; + area.angle = area.y, delete area.y; + area.startAngle = area.y0, delete area.y0; + area.endAngle = area.y1, delete area.y1; + return area; + }; + d3.svg.chord = function() { + var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; + function chord(d, i) { + var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); + return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; + } + function subgroup(self, f, d, i) { + var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset; + return { + r: r, + a0: a0, + a1: a1, + p0: [ r * Math.cos(a0), r * Math.sin(a0) ], + p1: [ r * Math.cos(a1), r * Math.sin(a1) ] + }; + } + function equals(a, b) { + return a.a0 == b.a0 && a.a1 == b.a1; + } + function arc(r, p, a) { + return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; + } + function curve(r0, p0, r1, p1) { + return "Q 0,0 " + p1; + } + chord.radius = function(v) { + if (!arguments.length) return radius; + radius = d3_functor(v); + return chord; + }; + chord.source = function(v) { + if (!arguments.length) return source; + source = d3_functor(v); + return chord; + }; + chord.target = function(v) { + if (!arguments.length) return target; + target = d3_functor(v); + return chord; + }; + chord.startAngle = function(v) { + if (!arguments.length) return startAngle; + startAngle = d3_functor(v); + return chord; + }; + chord.endAngle = function(v) { + if (!arguments.length) return endAngle; + endAngle = d3_functor(v); + return chord; + }; + return chord; + }; + function d3_svg_chordRadius(d) { + return d.radius; + } + d3.svg.diagonal = function() { + var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; + function diagonal(d, i) { + var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { + x: p0.x, + y: m + }, { + x: p3.x, + y: m + }, p3 ]; + p = p.map(projection); + return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; + } + diagonal.source = function(x) { + if (!arguments.length) return source; + source = d3_functor(x); + return diagonal; + }; + diagonal.target = function(x) { + if (!arguments.length) return target; + target = d3_functor(x); + return diagonal; + }; + diagonal.projection = function(x) { + if (!arguments.length) return projection; + projection = x; + return diagonal; + }; + return diagonal; + }; + function d3_svg_diagonalProjection(d) { + return [ d.x, d.y ]; + } + d3.svg.diagonal.radial = function() { + var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; + diagonal.projection = function(x) { + return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; + }; + return diagonal; + }; + function d3_svg_diagonalRadialProjection(projection) { + return function() { + var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset; + return [ r * Math.cos(a), r * Math.sin(a) ]; + }; + } + d3.svg.symbol = function() { + var type = d3_svg_symbolType, size = d3_svg_symbolSize; + function symbol(d, i) { + return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); + } + symbol.type = function(x) { + if (!arguments.length) return type; + type = d3_functor(x); + return symbol; + }; + symbol.size = function(x) { + if (!arguments.length) return size; + size = d3_functor(x); + return symbol; + }; + return symbol; + }; + function d3_svg_symbolSize() { + return 64; + } + function d3_svg_symbolType() { + return "circle"; + } + function d3_svg_symbolCircle(size) { + var r = Math.sqrt(size / π); + return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; + } + var d3_svg_symbols = d3.map({ + circle: d3_svg_symbolCircle, + cross: function(size) { + var r = Math.sqrt(size / 5) / 2; + return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; + }, + diamond: function(size) { + var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; + return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; + }, + square: function(size) { + var r = Math.sqrt(size) / 2; + return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; + }, + "triangle-down": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; + }, + "triangle-up": function(size) { + var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; + return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; + } + }); + d3.svg.symbolTypes = d3_svg_symbols.keys(); + var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); + d3.svg.axis = function() { + var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, tickMajorSize = 6, tickMinorSize = 6, tickEndSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_, tickSubdivide = 0; + function axis(g) { + g.each(function() { + var g = d3.select(this); + var ticks = tickValues == null ? scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain() : tickValues, tickFormat = tickFormat_ == null ? scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments_) : String : tickFormat_; + var subticks = d3_svg_axisSubdivide(scale, ticks, tickSubdivide), subtick = g.selectAll(".tick.minor").data(subticks, String), subtickEnter = subtick.enter().insert("line", ".tick").attr("class", "tick minor").style("opacity", 1e-6), subtickExit = d3.transition(subtick.exit()).style("opacity", 1e-6).remove(), subtickUpdate = d3.transition(subtick).style("opacity", 1); + var tick = g.selectAll(".tick.major").data(ticks, String), tickEnter = tick.enter().insert("g", "path").attr("class", "tick major").style("opacity", 1e-6), tickExit = d3.transition(tick.exit()).style("opacity", 1e-6).remove(), tickUpdate = d3.transition(tick).style("opacity", 1), tickTransform; + var range = d3_scaleRange(scale), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), + d3.transition(path)); + var scale1 = scale.copy(), scale0 = this.__chart__ || scale1; + this.__chart__ = scale1; + tickEnter.append("line"); + tickEnter.append("text"); + var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"); + switch (orient) { + case "bottom": + { + tickTransform = d3_svg_axisX; + subtickEnter.attr("y2", tickMinorSize); + subtickUpdate.attr("x2", 0).attr("y2", tickMinorSize); + lineEnter.attr("y2", tickMajorSize); + textEnter.attr("y", Math.max(tickMajorSize, 0) + tickPadding); + lineUpdate.attr("x2", 0).attr("y2", tickMajorSize); + textUpdate.attr("x", 0).attr("y", Math.max(tickMajorSize, 0) + tickPadding); + text.attr("dy", ".71em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + tickEndSize + "V0H" + range[1] + "V" + tickEndSize); + break; + } + + case "top": + { + tickTransform = d3_svg_axisX; + subtickEnter.attr("y2", -tickMinorSize); + subtickUpdate.attr("x2", 0).attr("y2", -tickMinorSize); + lineEnter.attr("y2", -tickMajorSize); + textEnter.attr("y", -(Math.max(tickMajorSize, 0) + tickPadding)); + lineUpdate.attr("x2", 0).attr("y2", -tickMajorSize); + textUpdate.attr("x", 0).attr("y", -(Math.max(tickMajorSize, 0) + tickPadding)); + text.attr("dy", "0em").style("text-anchor", "middle"); + pathUpdate.attr("d", "M" + range[0] + "," + -tickEndSize + "V0H" + range[1] + "V" + -tickEndSize); + break; + } + + case "left": + { + tickTransform = d3_svg_axisY; + subtickEnter.attr("x2", -tickMinorSize); + subtickUpdate.attr("x2", -tickMinorSize).attr("y2", 0); + lineEnter.attr("x2", -tickMajorSize); + textEnter.attr("x", -(Math.max(tickMajorSize, 0) + tickPadding)); + lineUpdate.attr("x2", -tickMajorSize).attr("y2", 0); + textUpdate.attr("x", -(Math.max(tickMajorSize, 0) + tickPadding)).attr("y", 0); + text.attr("dy", ".32em").style("text-anchor", "end"); + pathUpdate.attr("d", "M" + -tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + -tickEndSize); + break; + } + + case "right": + { + tickTransform = d3_svg_axisY; + subtickEnter.attr("x2", tickMinorSize); + subtickUpdate.attr("x2", tickMinorSize).attr("y2", 0); + lineEnter.attr("x2", tickMajorSize); + textEnter.attr("x", Math.max(tickMajorSize, 0) + tickPadding); + lineUpdate.attr("x2", tickMajorSize).attr("y2", 0); + textUpdate.attr("x", Math.max(tickMajorSize, 0) + tickPadding).attr("y", 0); + text.attr("dy", ".32em").style("text-anchor", "start"); + pathUpdate.attr("d", "M" + tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + tickEndSize); + break; + } + } + if (scale.ticks) { + tickEnter.call(tickTransform, scale0); + tickUpdate.call(tickTransform, scale1); + tickExit.call(tickTransform, scale1); + subtickEnter.call(tickTransform, scale0); + subtickUpdate.call(tickTransform, scale1); + subtickExit.call(tickTransform, scale1); + } else { + var dx = scale1.rangeBand() / 2, x = function(d) { + return scale1(d) + dx; + }; + tickEnter.call(tickTransform, x); + tickUpdate.call(tickTransform, x); + } + }); + } + axis.scale = function(x) { + if (!arguments.length) return scale; + scale = x; + return axis; + }; + axis.orient = function(x) { + if (!arguments.length) return orient; + orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; + return axis; + }; + axis.ticks = function() { + if (!arguments.length) return tickArguments_; + tickArguments_ = arguments; + return axis; + }; + axis.tickValues = function(x) { + if (!arguments.length) return tickValues; + tickValues = x; + return axis; + }; + axis.tickFormat = function(x) { + if (!arguments.length) return tickFormat_; + tickFormat_ = x; + return axis; + }; + axis.tickSize = function(x, y) { + if (!arguments.length) return tickMajorSize; + var n = arguments.length - 1; + tickMajorSize = +x; + tickMinorSize = n > 1 ? +y : tickMajorSize; + tickEndSize = n > 0 ? +arguments[n] : tickMajorSize; + return axis; + }; + axis.tickPadding = function(x) { + if (!arguments.length) return tickPadding; + tickPadding = +x; + return axis; + }; + axis.tickSubdivide = function(x) { + if (!arguments.length) return tickSubdivide; + tickSubdivide = +x; + return axis; + }; + return axis; + }; + var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { + top: 1, + right: 1, + bottom: 1, + left: 1 + }; + function d3_svg_axisX(selection, x) { + selection.attr("transform", function(d) { + return "translate(" + x(d) + ",0)"; + }); + } + function d3_svg_axisY(selection, y) { + selection.attr("transform", function(d) { + return "translate(0," + y(d) + ")"; + }); + } + function d3_svg_axisSubdivide(scale, ticks, m) { + subticks = []; + if (m && ticks.length > 1) { + var extent = d3_scaleExtent(scale.domain()), subticks, i = -1, n = ticks.length, d = (ticks[1] - ticks[0]) / ++m, j, v; + while (++i < n) { + for (j = m; --j > 0; ) { + if ((v = +ticks[i] - j * d) >= extent[0]) { + subticks.push(v); + } + } + } + for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1]; ) { + subticks.push(v); + } + } + return subticks; + } + d3.svg.brush = function() { + var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, resizes = d3_svg_brushResizes[0], extent = [ [ 0, 0 ], [ 0, 0 ] ], extentDomain; + function brush(g) { + g.each(function() { + var g = d3.select(this), bg = g.selectAll(".background").data([ 0 ]), fg = g.selectAll(".extent").data([ 0 ]), tz = g.selectAll(".resize").data(resizes, String), e; + g.style("pointer-events", "all").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); + bg.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); + fg.enter().append("rect").attr("class", "extent").style("cursor", "move"); + tz.enter().append("g").attr("class", function(d) { + return "resize " + d; + }).style("cursor", function(d) { + return d3_svg_brushCursor[d]; + }).append("rect").attr("x", function(d) { + return /[ew]$/.test(d) ? -3 : null; + }).attr("y", function(d) { + return /^[ns]/.test(d) ? -3 : null; + }).attr("width", 6).attr("height", 6).style("visibility", "hidden"); + tz.style("display", brush.empty() ? "none" : null); + tz.exit().remove(); + if (x) { + e = d3_scaleRange(x); + bg.attr("x", e[0]).attr("width", e[1] - e[0]); + redrawX(g); + } + if (y) { + e = d3_scaleRange(y); + bg.attr("y", e[0]).attr("height", e[1] - e[0]); + redrawY(g); + } + redraw(g); + }); + } + function redraw(g) { + g.selectAll(".resize").attr("transform", function(d) { + return "translate(" + extent[+/e$/.test(d)][0] + "," + extent[+/^s/.test(d)][1] + ")"; + }); + } + function redrawX(g) { + g.select(".extent").attr("x", extent[0][0]); + g.selectAll(".extent,.n>rect,.s>rect").attr("width", extent[1][0] - extent[0][0]); + } + function redrawY(g) { + g.select(".extent").attr("y", extent[0][1]); + g.selectAll(".extent,.e>rect,.w>rect").attr("height", extent[1][1] - extent[0][1]); + } + function brushstart() { + var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), center, origin = mouse(), offset; + var w = d3.select(d3_window).on("mousemove.brush", brushmove).on("mouseup.brush", brushend).on("touchmove.brush", brushmove).on("touchend.brush", brushend).on("keydown.brush", keydown).on("keyup.brush", keyup); + if (dragging) { + origin[0] = extent[0][0] - origin[0]; + origin[1] = extent[0][1] - origin[1]; + } else if (resizing) { + var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); + offset = [ extent[1 - ex][0] - origin[0], extent[1 - ey][1] - origin[1] ]; + origin[0] = extent[ex][0]; + origin[1] = extent[ey][1]; + } else if (d3.event.altKey) center = origin.slice(); + g.style("pointer-events", "none").selectAll(".resize").style("display", null); + d3.select("body").style("cursor", eventTarget.style("cursor")); + event_({ + type: "brushstart" + }); + brushmove(); + d3_eventCancel(); + function mouse() { + var touches = d3.event.changedTouches; + return touches ? d3.touches(target, touches)[0] : d3.mouse(target); + } + function keydown() { + if (d3.event.keyCode == 32) { + if (!dragging) { + center = null; + origin[0] -= extent[1][0]; + origin[1] -= extent[1][1]; + dragging = 2; + } + d3_eventCancel(); + } + } + function keyup() { + if (d3.event.keyCode == 32 && dragging == 2) { + origin[0] += extent[1][0]; + origin[1] += extent[1][1]; + dragging = 0; + d3_eventCancel(); + } + } + function brushmove() { + var point = mouse(), moved = false; + if (offset) { + point[0] += offset[0]; + point[1] += offset[1]; + } + if (!dragging) { + if (d3.event.altKey) { + if (!center) center = [ (extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2 ]; + origin[0] = extent[+(point[0] < center[0])][0]; + origin[1] = extent[+(point[1] < center[1])][1]; + } else center = null; + } + if (resizingX && move1(point, x, 0)) { + redrawX(g); + moved = true; + } + if (resizingY && move1(point, y, 1)) { + redrawY(g); + moved = true; + } + if (moved) { + redraw(g); + event_({ + type: "brush", + mode: dragging ? "move" : "resize" + }); + } + } + function move1(point, scale, i) { + var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], size = extent[1][i] - extent[0][i], min, max; + if (dragging) { + r0 -= position; + r1 -= size + position; + } + min = Math.max(r0, Math.min(r1, point[i])); + if (dragging) { + max = (min += position) + size; + } else { + if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); + if (position < min) { + max = min; + min = position; + } else { + max = position; + } + } + if (extent[0][i] !== min || extent[1][i] !== max) { + extentDomain = null; + extent[0][i] = min; + extent[1][i] = max; + return true; + } + } + function brushend() { + brushmove(); + g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); + d3.select("body").style("cursor", null); + w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); + event_({ + type: "brushend" + }); + d3_eventCancel(); + } + } + brush.x = function(z) { + if (!arguments.length) return x; + x = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.y = function(z) { + if (!arguments.length) return y; + y = z; + resizes = d3_svg_brushResizes[!x << 1 | !y]; + return brush; + }; + brush.extent = function(z) { + var x0, x1, y0, y1, t; + if (!arguments.length) { + z = extentDomain || extent; + if (x) { + x0 = z[0][0], x1 = z[1][0]; + if (!extentDomain) { + x0 = extent[0][0], x1 = extent[1][0]; + if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + } + } + if (y) { + y0 = z[0][1], y1 = z[1][1]; + if (!extentDomain) { + y0 = extent[0][1], y1 = extent[1][1]; + if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + } + } + return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; + } + extentDomain = [ [ 0, 0 ], [ 0, 0 ] ]; + if (x) { + x0 = z[0], x1 = z[1]; + if (y) x0 = x0[0], x1 = x1[0]; + extentDomain[0][0] = x0, extentDomain[1][0] = x1; + if (x.invert) x0 = x(x0), x1 = x(x1); + if (x1 < x0) t = x0, x0 = x1, x1 = t; + extent[0][0] = x0 | 0, extent[1][0] = x1 | 0; + } + if (y) { + y0 = z[0], y1 = z[1]; + if (x) y0 = y0[1], y1 = y1[1]; + extentDomain[0][1] = y0, extentDomain[1][1] = y1; + if (y.invert) y0 = y(y0), y1 = y(y1); + if (y1 < y0) t = y0, y0 = y1, y1 = t; + extent[0][1] = y0 | 0, extent[1][1] = y1 | 0; + } + return brush; + }; + brush.clear = function() { + extentDomain = null; + extent[0][0] = extent[0][1] = extent[1][0] = extent[1][1] = 0; + return brush; + }; + brush.empty = function() { + return x && extent[0][0] === extent[1][0] || y && extent[0][1] === extent[1][1]; + }; + return d3.rebind(brush, event, "on"); + }; + var d3_svg_brushCursor = { + n: "ns-resize", + e: "ew-resize", + s: "ns-resize", + w: "ew-resize", + nw: "nwse-resize", + ne: "nesw-resize", + se: "nwse-resize", + sw: "nesw-resize" + }; + var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; + d3.behavior = {}; + d3.behavior.drag = function() { + var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null; + function drag() { + this.on("mousedown.drag", mousedown).on("touchstart.drag", mousedown); + } + function mousedown() { + var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null, offset, origin_ = point(), moved = 0; + var w = d3.select(d3_window).on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true); + if (origin) { + offset = origin.apply(target, arguments); + offset = [ offset.x - origin_[0], offset.y - origin_[1] ]; + } else { + offset = [ 0, 0 ]; + } + if (touchId == null) d3_eventCancel(); + event_({ + type: "dragstart" + }); + function point() { + var p = target.parentNode; + return touchId != null ? d3.touches(p).filter(function(p) { + return p.identifier === touchId; + })[0] : d3.mouse(p); + } + function dragmove() { + if (!target.parentNode) return dragend(); + var p = point(), dx = p[0] - origin_[0], dy = p[1] - origin_[1]; + moved |= dx | dy; + origin_ = p; + d3_eventCancel(); + event_({ + type: "drag", + x: p[0] + offset[0], + y: p[1] + offset[1], + dx: dx, + dy: dy + }); + } + function dragend() { + event_({ + type: "dragend" + }); + if (moved) { + d3_eventCancel(); + if (d3.event.target === eventTarget) w.on("click.drag", click, true); + } + w.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null); + } + function click() { + d3_eventCancel(); + w.on("click.drag", null); + } + } + drag.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return drag; + }; + return d3.rebind(drag, event, "on"); + }; + d3.behavior.zoom = function() { + var translate = [ 0, 0 ], translate0, scale = 1, scale0, scaleExtent = d3_behavior_zoomInfinity, event = d3_eventDispatch(zoom, "zoom"), x0, x1, y0, y1, touchtime; + function zoom() { + this.on("mousedown.zoom", mousedown).on("mousemove.zoom", mousemove).on(d3_behavior_zoomWheel + ".zoom", mousewheel).on("dblclick.zoom", dblclick).on("touchstart.zoom", touchstart).on("touchmove.zoom", touchmove).on("touchend.zoom", touchstart); + } + zoom.translate = function(x) { + if (!arguments.length) return translate; + translate = x.map(Number); + rescale(); + return zoom; + }; + zoom.scale = function(x) { + if (!arguments.length) return scale; + scale = +x; + rescale(); + return zoom; + }; + zoom.scaleExtent = function(x) { + if (!arguments.length) return scaleExtent; + scaleExtent = x == null ? d3_behavior_zoomInfinity : x.map(Number); + return zoom; + }; + zoom.x = function(z) { + if (!arguments.length) return x1; + x1 = z; + x0 = z.copy(); + translate = [ 0, 0 ]; + scale = 1; + return zoom; + }; + zoom.y = function(z) { + if (!arguments.length) return y1; + y1 = z; + y0 = z.copy(); + translate = [ 0, 0 ]; + scale = 1; + return zoom; + }; + function location(p) { + return [ (p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale ]; + } + function point(l) { + return [ l[0] * scale + translate[0], l[1] * scale + translate[1] ]; + } + function scaleTo(s) { + scale = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); + } + function translateTo(p, l) { + l = point(l); + translate[0] += p[0] - l[0]; + translate[1] += p[1] - l[1]; + } + function rescale() { + if (x1) x1.domain(x0.range().map(function(x) { + return (x - translate[0]) / scale; + }).map(x0.invert)); + if (y1) y1.domain(y0.range().map(function(y) { + return (y - translate[1]) / scale; + }).map(y0.invert)); + } + function dispatch(event) { + rescale(); + d3.event.preventDefault(); + event({ + type: "zoom", + scale: scale, + translate: translate + }); + } + function mousedown() { + var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, moved = 0, w = d3.select(d3_window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup), l = location(d3.mouse(target)); + d3_window.focus(); + d3_eventCancel(); + function mousemove() { + moved = 1; + translateTo(d3.mouse(target), l); + dispatch(event_); + } + function mouseup() { + if (moved) d3_eventCancel(); + w.on("mousemove.zoom", null).on("mouseup.zoom", null); + if (moved && d3.event.target === eventTarget) w.on("click.zoom", click, true); + } + function click() { + d3_eventCancel(); + w.on("click.zoom", null); + } + } + function mousewheel() { + if (!translate0) translate0 = location(d3.mouse(this)); + scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * scale); + translateTo(d3.mouse(this), translate0); + dispatch(event.of(this, arguments)); + } + function mousemove() { + translate0 = null; + } + function dblclick() { + var p = d3.mouse(this), l = location(p), k = Math.log(scale) / Math.LN2; + scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1)); + translateTo(p, l); + dispatch(event.of(this, arguments)); + } + function touchstart() { + var touches = d3.touches(this), now = Date.now(); + scale0 = scale; + translate0 = {}; + touches.forEach(function(t) { + translate0[t.identifier] = location(t); + }); + d3_eventCancel(); + if (touches.length === 1) { + if (now - touchtime < 500) { + var p = touches[0], l = location(touches[0]); + scaleTo(scale * 2); + translateTo(p, l); + dispatch(event.of(this, arguments)); + } + touchtime = now; + } + } + function touchmove() { + var touches = d3.touches(this), p0 = touches[0], l0 = translate0[p0.identifier]; + if (p1 = touches[1]) { + var p1, l1 = translate0[p1.identifier]; + p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; + l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; + scaleTo(d3.event.scale * scale0); + } + translateTo(p0, l0); + touchtime = null; + dispatch(event.of(this, arguments)); + } + return d3.rebind(zoom, event, "on"); + }; + var d3_behavior_zoomInfinity = [ 0, Infinity ]; + var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in document ? (d3_behavior_zoomDelta = function() { + return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); + }, "wheel") : "onmousewheel" in document ? (d3_behavior_zoomDelta = function() { + return d3.event.wheelDelta; + }, "mousewheel") : (d3_behavior_zoomDelta = function() { + return -d3.event.detail; + }, "MozMousePixelScroll"); + d3.layout = {}; + d3.layout.bundle = function() { + return function(links) { + var paths = [], i = -1, n = links.length; + while (++i < n) paths.push(d3_layout_bundlePath(links[i])); + return paths; + }; + }; + function d3_layout_bundlePath(link) { + var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; + while (start !== lca) { + start = start.parent; + points.push(start); + } + var k = points.length; + while (end !== lca) { + points.splice(k, 0, end); + end = end.parent; + } + return points; + } + function d3_layout_bundleAncestors(node) { + var ancestors = [], parent = node.parent; + while (parent != null) { + ancestors.push(node); + node = parent; + parent = parent.parent; + } + ancestors.push(node); + return ancestors; + } + function d3_layout_bundleLeastCommonAncestor(a, b) { + if (a === b) return a; + var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; + while (aNode === bNode) { + sharedNode = aNode; + aNode = aNodes.pop(); + bNode = bNodes.pop(); + } + return sharedNode; + } + d3.layout.chord = function() { + var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; + function relayout() { + var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; + chords = []; + groups = []; + k = 0, i = -1; + while (++i < n) { + x = 0, j = -1; + while (++j < n) { + x += matrix[i][j]; + } + groupSums.push(x); + subgroupIndex.push(d3.range(n)); + k += x; + } + if (sortGroups) { + groupIndex.sort(function(a, b) { + return sortGroups(groupSums[a], groupSums[b]); + }); + } + if (sortSubgroups) { + subgroupIndex.forEach(function(d, i) { + d.sort(function(a, b) { + return sortSubgroups(matrix[i][a], matrix[i][b]); + }); + }); + } + k = (2 * π - padding * n) / k; + x = 0, i = -1; + while (++i < n) { + x0 = x, j = -1; + while (++j < n) { + var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; + subgroups[di + "-" + dj] = { + index: di, + subindex: dj, + startAngle: a0, + endAngle: a1, + value: v + }; + } + groups[di] = { + index: di, + startAngle: x0, + endAngle: x, + value: (x - x0) / k + }; + x += padding; + } + i = -1; + while (++i < n) { + j = i - 1; + while (++j < n) { + var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; + if (source.value || target.value) { + chords.push(source.value < target.value ? { + source: target, + target: source + } : { + source: source, + target: target + }); + } + } + } + if (sortChords) resort(); + } + function resort() { + chords.sort(function(a, b) { + return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); + }); + } + chord.matrix = function(x) { + if (!arguments.length) return matrix; + n = (matrix = x) && matrix.length; + chords = groups = null; + return chord; + }; + chord.padding = function(x) { + if (!arguments.length) return padding; + padding = x; + chords = groups = null; + return chord; + }; + chord.sortGroups = function(x) { + if (!arguments.length) return sortGroups; + sortGroups = x; + chords = groups = null; + return chord; + }; + chord.sortSubgroups = function(x) { + if (!arguments.length) return sortSubgroups; + sortSubgroups = x; + chords = null; + return chord; + }; + chord.sortChords = function(x) { + if (!arguments.length) return sortChords; + sortChords = x; + if (chords) resort(); + return chord; + }; + chord.chords = function() { + if (!chords) relayout(); + return chords; + }; + chord.groups = function() { + if (!groups) relayout(); + return groups; + }; + return chord; + }; + d3.layout.force = function() { + var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, gravity = .1, theta = .8, nodes = [], links = [], distances, strengths, charges; + function repulse(node) { + return function(quad, x1, _, x2) { + if (quad.point !== node) { + var dx = quad.cx - node.x, dy = quad.cy - node.y, dn = 1 / Math.sqrt(dx * dx + dy * dy); + if ((x2 - x1) * dn < theta) { + var k = quad.charge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + return true; + } + if (quad.point && isFinite(dn)) { + var k = quad.pointCharge * dn * dn; + node.px -= dx * k; + node.py -= dy * k; + } + } + return !quad.charge; + }; + } + force.tick = function() { + if ((alpha *= .99) < .005) { + event.end({ + type: "end", + alpha: alpha = 0 + }); + return true; + } + var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; + for (i = 0; i < m; ++i) { + o = links[i]; + s = o.source; + t = o.target; + x = t.x - s.x; + y = t.y - s.y; + if (l = x * x + y * y) { + l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; + x *= l; + y *= l; + t.x -= x * (k = s.weight / (t.weight + s.weight)); + t.y -= y * k; + s.x += x * (k = 1 - k); + s.y += y * k; + } + } + if (k = alpha * gravity) { + x = size[0] / 2; + y = size[1] / 2; + i = -1; + if (k) while (++i < n) { + o = nodes[i]; + o.x += (x - o.x) * k; + o.y += (y - o.y) * k; + } + } + if (charge) { + d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); + i = -1; + while (++i < n) { + if (!(o = nodes[i]).fixed) { + q.visit(repulse(o)); + } + } + } + i = -1; + while (++i < n) { + o = nodes[i]; + if (o.fixed) { + o.x = o.px; + o.y = o.py; + } else { + o.x -= (o.px - (o.px = o.x)) * friction; + o.y -= (o.py - (o.py = o.y)) * friction; + } + } + event.tick({ + type: "tick", + alpha: alpha + }); + }; + force.nodes = function(x) { + if (!arguments.length) return nodes; + nodes = x; + return force; + }; + force.links = function(x) { + if (!arguments.length) return links; + links = x; + return force; + }; + force.size = function(x) { + if (!arguments.length) return size; + size = x; + return force; + }; + force.linkDistance = function(x) { + if (!arguments.length) return linkDistance; + linkDistance = typeof x === "function" ? x : +x; + return force; + }; + force.distance = force.linkDistance; + force.linkStrength = function(x) { + if (!arguments.length) return linkStrength; + linkStrength = typeof x === "function" ? x : +x; + return force; + }; + force.friction = function(x) { + if (!arguments.length) return friction; + friction = +x; + return force; + }; + force.charge = function(x) { + if (!arguments.length) return charge; + charge = typeof x === "function" ? x : +x; + return force; + }; + force.gravity = function(x) { + if (!arguments.length) return gravity; + gravity = +x; + return force; + }; + force.theta = function(x) { + if (!arguments.length) return theta; + theta = +x; + return force; + }; + force.alpha = function(x) { + if (!arguments.length) return alpha; + x = +x; + if (alpha) { + if (x > 0) alpha = x; else alpha = 0; + } else if (x > 0) { + event.start({ + type: "start", + alpha: alpha = x + }); + d3.timer(force.tick); + } + return force; + }; + force.start = function() { + var i, j, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; + for (i = 0; i < n; ++i) { + (o = nodes[i]).index = i; + o.weight = 0; + } + for (i = 0; i < m; ++i) { + o = links[i]; + if (typeof o.source == "number") o.source = nodes[o.source]; + if (typeof o.target == "number") o.target = nodes[o.target]; + ++o.source.weight; + ++o.target.weight; + } + for (i = 0; i < n; ++i) { + o = nodes[i]; + if (isNaN(o.x)) o.x = position("x", w); + if (isNaN(o.y)) o.y = position("y", h); + if (isNaN(o.px)) o.px = o.x; + if (isNaN(o.py)) o.py = o.y; + } + distances = []; + if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; + strengths = []; + if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; + charges = []; + if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; + function position(dimension, size) { + var neighbors = neighbor(i), j = -1, m = neighbors.length, x; + while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x; + return Math.random() * size; + } + function neighbor() { + if (!neighbors) { + neighbors = []; + for (j = 0; j < n; ++j) { + neighbors[j] = []; + } + for (j = 0; j < m; ++j) { + var o = links[j]; + neighbors[o.source.index].push(o.target); + neighbors[o.target.index].push(o.source); + } + } + return neighbors[i]; + } + return force.resume(); + }; + force.resume = function() { + return force.alpha(.1); + }; + force.stop = function() { + return force.alpha(0); + }; + force.drag = function() { + if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); + if (!arguments.length) return drag; + this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); + }; + function dragmove(d) { + d.px = d3.event.x, d.py = d3.event.y; + force.resume(); + } + return d3.rebind(force, event, "on"); + }; + function d3_layout_forceDragstart(d) { + d.fixed |= 2; + } + function d3_layout_forceDragend(d) { + d.fixed &= ~6; + } + function d3_layout_forceMouseover(d) { + d.fixed |= 4; + d.px = d.x, d.py = d.y; + } + function d3_layout_forceMouseout(d) { + d.fixed &= ~4; + } + function d3_layout_forceAccumulate(quad, alpha, charges) { + var cx = 0, cy = 0; + quad.charge = 0; + if (!quad.leaf) { + var nodes = quad.nodes, n = nodes.length, i = -1, c; + while (++i < n) { + c = nodes[i]; + if (c == null) continue; + d3_layout_forceAccumulate(c, alpha, charges); + quad.charge += c.charge; + cx += c.charge * c.cx; + cy += c.charge * c.cy; + } + } + if (quad.point) { + if (!quad.leaf) { + quad.point.x += Math.random() - .5; + quad.point.y += Math.random() - .5; + } + var k = alpha * charges[quad.point.index]; + quad.charge += quad.pointCharge = k; + cx += k * quad.point.x; + cy += k * quad.point.y; + } + quad.cx = cx / quad.charge; + quad.cy = cy / quad.charge; + } + var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1; + d3.layout.partition = function() { + var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; + function position(node, x, dx, dy) { + var children = node.children; + node.x = x; + node.y = node.depth * dy; + node.dx = dx; + node.dy = dy; + if (children && (n = children.length)) { + var i = -1, n, c, d; + dx = node.value ? dx / node.value : 0; + while (++i < n) { + position(c = children[i], x, d = c.value * dx, dy); + x += d; + } + } + } + function depth(node) { + var children = node.children, d = 0; + if (children && (n = children.length)) { + var i = -1, n; + while (++i < n) d = Math.max(d, depth(children[i])); + } + return 1 + d; + } + function partition(d, i) { + var nodes = hierarchy.call(this, d, i); + position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); + return nodes; + } + partition.size = function(x) { + if (!arguments.length) return size; + size = x; + return partition; + }; + return d3_layout_hierarchyRebind(partition, hierarchy); + }; + d3.layout.pie = function() { + var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = 2 * π; + function pie(data) { + var values = data.map(function(d, i) { + return +value.call(pie, d, i); + }); + var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle); + var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - startAngle) / d3.sum(values); + var index = d3.range(data.length); + if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { + return values[j] - values[i]; + } : function(i, j) { + return sort(data[i], data[j]); + }); + var arcs = []; + index.forEach(function(i) { + var d; + arcs[i] = { + data: data[i], + value: d = values[i], + startAngle: a, + endAngle: a += d * k + }; + }); + return arcs; + } + pie.value = function(x) { + if (!arguments.length) return value; + value = x; + return pie; + }; + pie.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return pie; + }; + pie.startAngle = function(x) { + if (!arguments.length) return startAngle; + startAngle = x; + return pie; + }; + pie.endAngle = function(x) { + if (!arguments.length) return endAngle; + endAngle = x; + return pie; + }; + return pie; + }; + var d3_layout_pieSortByValue = {}; + d3.layout.stack = function() { + var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; + function stack(data, index) { + var series = data.map(function(d, i) { + return values.call(stack, d, i); + }); + var points = series.map(function(d) { + return d.map(function(v, i) { + return [ x.call(stack, v, i), y.call(stack, v, i) ]; + }); + }); + var orders = order.call(stack, points, index); + series = d3.permute(series, orders); + points = d3.permute(points, orders); + var offsets = offset.call(stack, points, index); + var n = series.length, m = series[0].length, i, j, o; + for (j = 0; j < m; ++j) { + out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); + for (i = 1; i < n; ++i) { + out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); + } + } + return data; + } + stack.values = function(x) { + if (!arguments.length) return values; + values = x; + return stack; + }; + stack.order = function(x) { + if (!arguments.length) return order; + order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; + return stack; + }; + stack.offset = function(x) { + if (!arguments.length) return offset; + offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; + return stack; + }; + stack.x = function(z) { + if (!arguments.length) return x; + x = z; + return stack; + }; + stack.y = function(z) { + if (!arguments.length) return y; + y = z; + return stack; + }; + stack.out = function(z) { + if (!arguments.length) return out; + out = z; + return stack; + }; + return stack; + }; + function d3_layout_stackX(d) { + return d.x; + } + function d3_layout_stackY(d) { + return d.y; + } + function d3_layout_stackOut(d, y0, y) { + d.y0 = y0; + d.y = y; + } + var d3_layout_stackOrders = d3.map({ + "inside-out": function(data) { + var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { + return max[a] - max[b]; + }), top = 0, bottom = 0, tops = [], bottoms = []; + for (i = 0; i < n; ++i) { + j = index[i]; + if (top < bottom) { + top += sums[j]; + tops.push(j); + } else { + bottom += sums[j]; + bottoms.push(j); + } + } + return bottoms.reverse().concat(tops); + }, + reverse: function(data) { + return d3.range(data.length).reverse(); + }, + "default": d3_layout_stackOrderDefault + }); + var d3_layout_stackOffsets = d3.map({ + silhouette: function(data) { + var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o > max) max = o; + sums.push(o); + } + for (j = 0; j < m; ++j) { + y0[j] = (max - sums[j]) / 2; + } + return y0; + }, + wiggle: function(data) { + var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; + y0[0] = o = o0 = 0; + for (j = 1; j < m; ++j) { + for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; + for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { + for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { + s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; + } + s2 += s3 * data[i][j][1]; + } + y0[j] = o -= s1 ? s2 / s1 * dx : 0; + if (o < o0) o0 = o; + } + for (j = 0; j < m; ++j) y0[j] -= o0; + return y0; + }, + expand: function(data) { + var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; + for (j = 0; j < m; ++j) { + for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; + if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; + } + for (j = 0; j < m; ++j) y0[j] = 0; + return y0; + }, + zero: d3_layout_stackOffsetZero + }); + function d3_layout_stackOrderDefault(data) { + return d3.range(data.length); + } + function d3_layout_stackOffsetZero(data) { + var j = -1, m = data[0].length, y0 = []; + while (++j < m) y0[j] = 0; + return y0; + } + function d3_layout_stackMaxIndex(array) { + var i = 1, j = 0, v = array[0][1], k, n = array.length; + for (;i < n; ++i) { + if ((k = array[i][1]) > v) { + j = i; + v = k; + } + } + return j; + } + function d3_layout_stackReduceSum(d) { + return d.reduce(d3_layout_stackSum, 0); + } + function d3_layout_stackSum(p, d) { + return p + d[1]; + } + d3.layout.histogram = function() { + var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; + function histogram(data, i) { + var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; + while (++i < m) { + bin = bins[i] = []; + bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); + bin.y = 0; + } + if (m > 0) { + i = -1; + while (++i < n) { + x = values[i]; + if (x >= range[0] && x <= range[1]) { + bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; + bin.y += k; + bin.push(data[i]); + } + } + } + return bins; + } + histogram.value = function(x) { + if (!arguments.length) return valuer; + valuer = x; + return histogram; + }; + histogram.range = function(x) { + if (!arguments.length) return ranger; + ranger = d3_functor(x); + return histogram; + }; + histogram.bins = function(x) { + if (!arguments.length) return binner; + binner = typeof x === "number" ? function(range) { + return d3_layout_histogramBinFixed(range, x); + } : d3_functor(x); + return histogram; + }; + histogram.frequency = function(x) { + if (!arguments.length) return frequency; + frequency = !!x; + return histogram; + }; + return histogram; + }; + function d3_layout_histogramBinSturges(range, values) { + return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); + } + function d3_layout_histogramBinFixed(range, n) { + var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; + while (++x <= n) f[x] = m * x + b; + return f; + } + function d3_layout_histogramRange(values) { + return [ d3.min(values), d3.max(values) ]; + } + d3.layout.hierarchy = function() { + var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; + function recurse(node, depth, nodes) { + var childs = children.call(hierarchy, node, depth); + node.depth = depth; + nodes.push(node); + if (childs && (n = childs.length)) { + var i = -1, n, c = node.children = [], v = 0, j = depth + 1, d; + while (++i < n) { + d = recurse(childs[i], j, nodes); + d.parent = node; + c.push(d); + v += d.value; + } + if (sort) c.sort(sort); + if (value) node.value = v; + } else if (value) { + node.value = +value.call(hierarchy, node, depth) || 0; + } + return node; + } + function revalue(node, depth) { + var children = node.children, v = 0; + if (children && (n = children.length)) { + var i = -1, n, j = depth + 1; + while (++i < n) v += revalue(children[i], j); + } else if (value) { + v = +value.call(hierarchy, node, depth) || 0; + } + if (value) node.value = v; + return v; + } + function hierarchy(d) { + var nodes = []; + recurse(d, 0, nodes); + return nodes; + } + hierarchy.sort = function(x) { + if (!arguments.length) return sort; + sort = x; + return hierarchy; + }; + hierarchy.children = function(x) { + if (!arguments.length) return children; + children = x; + return hierarchy; + }; + hierarchy.value = function(x) { + if (!arguments.length) return value; + value = x; + return hierarchy; + }; + hierarchy.revalue = function(root) { + revalue(root, 0); + return root; + }; + return hierarchy; + }; + function d3_layout_hierarchyRebind(object, hierarchy) { + d3.rebind(object, hierarchy, "sort", "children", "value"); + object.nodes = object; + object.links = d3_layout_hierarchyLinks; + return object; + } + function d3_layout_hierarchyChildren(d) { + return d.children; + } + function d3_layout_hierarchyValue(d) { + return d.value; + } + function d3_layout_hierarchySort(a, b) { + return b.value - a.value; + } + function d3_layout_hierarchyLinks(nodes) { + return d3.merge(nodes.map(function(parent) { + return (parent.children || []).map(function(child) { + return { + source: parent, + target: child + }; + }); + })); + } + d3.layout.pack = function() { + var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ]; + function pack(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0]; + root.x = 0; + root.y = 0; + d3_layout_treeVisitAfter(root, function(d) { + d.r = Math.sqrt(d.value); + }); + d3_layout_treeVisitAfter(root, d3_layout_packSiblings); + var w = size[0], h = size[1], k = Math.max(2 * root.r / w, 2 * root.r / h); + if (padding > 0) { + var dr = padding * k / 2; + d3_layout_treeVisitAfter(root, function(d) { + d.r += dr; + }); + d3_layout_treeVisitAfter(root, d3_layout_packSiblings); + d3_layout_treeVisitAfter(root, function(d) { + d.r -= dr; + }); + k = Math.max(2 * root.r / w, 2 * root.r / h); + } + d3_layout_packTransform(root, w / 2, h / 2, 1 / k); + return nodes; + } + pack.size = function(x) { + if (!arguments.length) return size; + size = x; + return pack; + }; + pack.padding = function(_) { + if (!arguments.length) return padding; + padding = +_; + return pack; + }; + return d3_layout_hierarchyRebind(pack, hierarchy); + }; + function d3_layout_packSort(a, b) { + return a.value - b.value; + } + function d3_layout_packInsert(a, b) { + var c = a._pack_next; + a._pack_next = b; + b._pack_prev = a; + b._pack_next = c; + c._pack_prev = b; + } + function d3_layout_packSplice(a, b) { + a._pack_next = b; + b._pack_prev = a; + } + function d3_layout_packIntersects(a, b) { + var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; + return dr * dr - dx * dx - dy * dy > .001; + } + function d3_layout_packSiblings(node) { + if (!(nodes = node.children) || !(n = nodes.length)) return; + var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; + function bound(node) { + xMin = Math.min(node.x - node.r, xMin); + xMax = Math.max(node.x + node.r, xMax); + yMin = Math.min(node.y - node.r, yMin); + yMax = Math.max(node.y + node.r, yMax); + } + nodes.forEach(d3_layout_packLink); + a = nodes[0]; + a.x = -a.r; + a.y = 0; + bound(a); + if (n > 1) { + b = nodes[1]; + b.x = b.r; + b.y = 0; + bound(b); + if (n > 2) { + c = nodes[2]; + d3_layout_packPlace(a, b, c); + bound(c); + d3_layout_packInsert(a, c); + a._pack_prev = c; + d3_layout_packInsert(c, b); + b = a._pack_next; + for (i = 3; i < n; i++) { + d3_layout_packPlace(a, b, c = nodes[i]); + var isect = 0, s1 = 1, s2 = 1; + for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { + if (d3_layout_packIntersects(j, c)) { + isect = 1; + break; + } + } + if (isect == 1) { + for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { + if (d3_layout_packIntersects(k, c)) { + break; + } + } + } + if (isect) { + if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); + i--; + } else { + d3_layout_packInsert(a, c); + b = c; + bound(c); + } + } + } + } + var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; + for (i = 0; i < n; i++) { + c = nodes[i]; + c.x -= cx; + c.y -= cy; + cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); + } + node.r = cr; + nodes.forEach(d3_layout_packUnlink); + } + function d3_layout_packLink(node) { + node._pack_next = node._pack_prev = node; + } + function d3_layout_packUnlink(node) { + delete node._pack_next; + delete node._pack_prev; + } + function d3_layout_packTransform(node, x, y, k) { + var children = node.children; + node.x = x += k * node.x; + node.y = y += k * node.y; + node.r *= k; + if (children) { + var i = -1, n = children.length; + while (++i < n) d3_layout_packTransform(children[i], x, y, k); + } + } + function d3_layout_packPlace(a, b, c) { + var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; + if (db && (dx || dy)) { + var da = b.r + c.r, dc = dx * dx + dy * dy; + da *= da; + db *= db; + var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); + c.x = a.x + x * dx + y * dy; + c.y = a.y + x * dy - y * dx; + } else { + c.x = a.x + db; + c.y = a.y; + } + } + d3.layout.cluster = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ]; + function cluster(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; + d3_layout_treeVisitAfter(root, function(node) { + var children = node.children; + if (children && children.length) { + node.x = d3_layout_clusterX(children); + node.y = d3_layout_clusterY(children); + } else { + node.x = previousNode ? x += separation(node, previousNode) : 0; + node.y = 0; + previousNode = node; + } + }); + var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; + d3_layout_treeVisitAfter(root, function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; + }); + return nodes; + } + cluster.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return cluster; + }; + cluster.size = function(x) { + if (!arguments.length) return size; + size = x; + return cluster; + }; + return d3_layout_hierarchyRebind(cluster, hierarchy); + }; + function d3_layout_clusterY(children) { + return 1 + d3.max(children, function(child) { + return child.y; + }); + } + function d3_layout_clusterX(children) { + return children.reduce(function(x, child) { + return x + child.x; + }, 0) / children.length; + } + function d3_layout_clusterLeft(node) { + var children = node.children; + return children && children.length ? d3_layout_clusterLeft(children[0]) : node; + } + function d3_layout_clusterRight(node) { + var children = node.children, n; + return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; + } + d3.layout.tree = function() { + var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ]; + function tree(d, i) { + var nodes = hierarchy.call(this, d, i), root = nodes[0]; + function firstWalk(node, previousSibling) { + var children = node.children, layout = node._tree; + if (children && (n = children.length)) { + var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1; + while (++i < n) { + child = children[i]; + firstWalk(child, previousChild); + ancestor = apportion(child, previousChild, ancestor); + previousChild = child; + } + d3_layout_treeShift(node); + var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim); + if (previousSibling) { + layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); + layout.mod = layout.prelim - midpoint; + } else { + layout.prelim = midpoint; + } + } else { + if (previousSibling) { + layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling); + } + } + } + function secondWalk(node, x) { + node.x = node._tree.prelim + x; + var children = node.children; + if (children && (n = children.length)) { + var i = -1, n; + x += node._tree.mod; + while (++i < n) { + secondWalk(children[i], x); + } + } + } + function apportion(node, previousSibling, ancestor) { + if (previousSibling) { + var vip = node, vop = node, vim = previousSibling, vom = node.parent.children[0], sip = vip._tree.mod, sop = vop._tree.mod, sim = vim._tree.mod, som = vom._tree.mod, shift; + while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { + vom = d3_layout_treeLeft(vom); + vop = d3_layout_treeRight(vop); + vop._tree.ancestor = node; + shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip); + if (shift > 0) { + d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift); + sip += shift; + sop += shift; + } + sim += vim._tree.mod; + sip += vip._tree.mod; + som += vom._tree.mod; + sop += vop._tree.mod; + } + if (vim && !d3_layout_treeRight(vop)) { + vop._tree.thread = vim; + vop._tree.mod += sim - sop; + } + if (vip && !d3_layout_treeLeft(vom)) { + vom._tree.thread = vip; + vom._tree.mod += sip - som; + ancestor = node; + } + } + return ancestor; + } + d3_layout_treeVisitAfter(root, function(node, previousSibling) { + node._tree = { + ancestor: node, + prelim: 0, + mod: 0, + change: 0, + shift: 0, + number: previousSibling ? previousSibling._tree.number + 1 : 0 + }; + }); + firstWalk(root); + secondWalk(root, -root._tree.prelim); + var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root, d3_layout_treeRightmost), deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = deep.depth || 1; + d3_layout_treeVisitAfter(root, function(node) { + node.x = (node.x - x0) / (x1 - x0) * size[0]; + node.y = node.depth / y1 * size[1]; + delete node._tree; + }); + return nodes; + } + tree.separation = function(x) { + if (!arguments.length) return separation; + separation = x; + return tree; + }; + tree.size = function(x) { + if (!arguments.length) return size; + size = x; + return tree; + }; + return d3_layout_hierarchyRebind(tree, hierarchy); + }; + function d3_layout_treeSeparation(a, b) { + return a.parent == b.parent ? 1 : 2; + } + function d3_layout_treeLeft(node) { + var children = node.children; + return children && children.length ? children[0] : node._tree.thread; + } + function d3_layout_treeRight(node) { + var children = node.children, n; + return children && (n = children.length) ? children[n - 1] : node._tree.thread; + } + function d3_layout_treeSearch(node, compare) { + var children = node.children; + if (children && (n = children.length)) { + var child, n, i = -1; + while (++i < n) { + if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) { + node = child; + } + } + } + return node; + } + function d3_layout_treeRightmost(a, b) { + return a.x - b.x; + } + function d3_layout_treeLeftmost(a, b) { + return b.x - a.x; + } + function d3_layout_treeDeepest(a, b) { + return a.depth - b.depth; + } + function d3_layout_treeVisitAfter(node, callback) { + function visit(node, previousSibling) { + var children = node.children; + if (children && (n = children.length)) { + var child, previousChild = null, i = -1, n; + while (++i < n) { + child = children[i]; + visit(child, previousChild); + previousChild = child; + } + } + callback(node, previousSibling); + } + visit(node, null); + } + function d3_layout_treeShift(node) { + var shift = 0, change = 0, children = node.children, i = children.length, child; + while (--i >= 0) { + child = children[i]._tree; + child.prelim += shift; + child.mod += shift; + shift += child.shift + (change += child.change); + } + } + function d3_layout_treeMove(ancestor, node, shift) { + ancestor = ancestor._tree; + node = node._tree; + var change = shift / (node.number - ancestor.number); + ancestor.change += change; + node.change -= change; + node.shift += shift; + node.prelim += shift; + node.mod += shift; + } + function d3_layout_treeAncestor(vim, node, ancestor) { + return vim._tree.ancestor.parent == node.parent ? vim._tree.ancestor : ancestor; + } + d3.layout.treemap = function() { + var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); + function scale(children, k) { + var i = -1, n = children.length, child, area; + while (++i < n) { + area = (child = children[i]).value * (k < 0 ? 0 : k); + child.area = isNaN(area) || area <= 0 ? 0 : area; + } + } + function squarify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while ((n = remaining.length) > 0) { + row.push(child = remaining[n - 1]); + row.area += child.area; + if (mode !== "squarify" || (score = worst(row, u)) <= best) { + remaining.pop(); + best = score; + } else { + row.area -= row.pop().area; + position(row, u, rect, false); + u = Math.min(rect.dx, rect.dy); + row.length = row.area = 0; + best = Infinity; + } + } + if (row.length) { + position(row, u, rect, true); + row.length = row.area = 0; + } + children.forEach(squarify); + } + } + function stickify(node) { + var children = node.children; + if (children && children.length) { + var rect = pad(node), remaining = children.slice(), child, row = []; + scale(remaining, rect.dx * rect.dy / node.value); + row.area = 0; + while (child = remaining.pop()) { + row.push(child); + row.area += child.area; + if (child.z != null) { + position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); + row.length = row.area = 0; + } + } + children.forEach(stickify); + } + } + function worst(row, u) { + var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; + while (++i < n) { + if (!(r = row[i].area)) continue; + if (r < rmin) rmin = r; + if (r > rmax) rmax = r; + } + s *= s; + u *= u; + return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; + } + function position(row, u, rect, flush) { + var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; + if (u == rect.dx) { + if (flush || v > rect.dy) v = rect.dy; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dy = v; + x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); + } + o.z = true; + o.dx += rect.x + rect.dx - x; + rect.y += v; + rect.dy -= v; + } else { + if (flush || v > rect.dx) v = rect.dx; + while (++i < n) { + o = row[i]; + o.x = x; + o.y = y; + o.dx = v; + y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); + } + o.z = false; + o.dy += rect.y + rect.dy - y; + rect.x += v; + rect.dx -= v; + } + } + function treemap(d) { + var nodes = stickies || hierarchy(d), root = nodes[0]; + root.x = 0; + root.y = 0; + root.dx = size[0]; + root.dy = size[1]; + if (stickies) hierarchy.revalue(root); + scale([ root ], root.dx * root.dy / root.value); + (stickies ? stickify : squarify)(root); + if (sticky) stickies = nodes; + return nodes; + } + treemap.size = function(x) { + if (!arguments.length) return size; + size = x; + return treemap; + }; + treemap.padding = function(x) { + if (!arguments.length) return padding; + function padFunction(node) { + var p = x.call(treemap, node, node.depth); + return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); + } + function padConstant(node) { + return d3_layout_treemapPad(node, x); + } + var type; + pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], + padConstant) : padConstant; + return treemap; + }; + treemap.round = function(x) { + if (!arguments.length) return round != Number; + round = x ? Math.round : Number; + return treemap; + }; + treemap.sticky = function(x) { + if (!arguments.length) return sticky; + sticky = x; + stickies = null; + return treemap; + }; + treemap.ratio = function(x) { + if (!arguments.length) return ratio; + ratio = x; + return treemap; + }; + treemap.mode = function(x) { + if (!arguments.length) return mode; + mode = x + ""; + return treemap; + }; + return d3_layout_hierarchyRebind(treemap, hierarchy); + }; + function d3_layout_treemapPadNull(node) { + return { + x: node.x, + y: node.y, + dx: node.dx, + dy: node.dy + }; + } + function d3_layout_treemapPad(node, padding) { + var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; + if (dx < 0) { + x += dx / 2; + dx = 0; + } + if (dy < 0) { + y += dy / 2; + dy = 0; + } + return { + x: x, + y: y, + dx: dx, + dy: dy + }; + } + function d3_dsv(delimiter, mimeType) { + var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); + function dsv(url, callback) { + return d3.xhr(url, mimeType, callback).response(response); + } + function response(request) { + return dsv.parse(request.responseText); + } + dsv.parse = function(text) { + var o; + return dsv.parseRows(text, function(row) { + if (o) return o(row); + o = new Function("d", "return {" + row.map(function(name, i) { + return JSON.stringify(name) + ": d[" + i + "]"; + }).join(",") + "}"); + }); + }; + dsv.parseRows = function(text, f) { + var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; + function token() { + if (I >= N) return EOF; + if (eol) return eol = false, EOL; + var j = I; + if (text.charCodeAt(j) === 34) { + var i = j; + while (i++ < N) { + if (text.charCodeAt(i) === 34) { + if (text.charCodeAt(i + 1) !== 34) break; + ++i; + } + } + I = i + 2; + var c = text.charCodeAt(i + 1); + if (c === 13) { + eol = true; + if (text.charCodeAt(i + 2) === 10) ++I; + } else if (c === 10) { + eol = true; + } + return text.substring(j + 1, i).replace(/""/g, '"'); + } + while (I < N) { + var c = text.charCodeAt(I++), k = 1; + if (c === 10) eol = true; else if (c === 13) { + eol = true; + if (text.charCodeAt(I) === 10) ++I, ++k; + } else if (c !== delimiterCode) continue; + return text.substring(j, I - k); + } + return text.substring(j); + } + while ((t = token()) !== EOF) { + var a = []; + while (t !== EOL && t !== EOF) { + a.push(t); + t = token(); + } + if (f && !(a = f(a, n++))) continue; + rows.push(a); + } + return rows; + }; + dsv.format = function(rows) { + return rows.map(formatRow).join("\n"); + }; + function formatRow(row) { + return row.map(formatValue).join(delimiter); + } + function formatValue(text) { + return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; + } + return dsv; + } + d3.csv = d3_dsv(",", "text/csv"); + d3.tsv = d3_dsv(" ", "text/tab-separated-values"); + d3.geo = {}; + d3.geo.stream = function(object, listener) { + if (d3_geo_streamObjectType.hasOwnProperty(object.type)) { + d3_geo_streamObjectType[object.type](object, listener); + } else { + d3_geo_streamGeometry(object, listener); + } + }; + function d3_geo_streamGeometry(geometry, listener) { + if (d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { + d3_geo_streamGeometryType[geometry.type](geometry, listener); + } + } + var d3_geo_streamObjectType = { + Feature: function(feature, listener) { + d3_geo_streamGeometry(feature.geometry, listener); + }, + FeatureCollection: function(object, listener) { + var features = object.features, i = -1, n = features.length; + while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); + } + }; + var d3_geo_streamGeometryType = { + Sphere: function(object, listener) { + listener.sphere(); + }, + Point: function(object, listener) { + var coordinate = object.coordinates; + listener.point(coordinate[0], coordinate[1]); + }, + MultiPoint: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length, coordinate; + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]); + }, + LineString: function(object, listener) { + d3_geo_streamLine(object.coordinates, listener, 0); + }, + MultiLineString: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); + }, + Polygon: function(object, listener) { + d3_geo_streamPolygon(object.coordinates, listener); + }, + MultiPolygon: function(object, listener) { + var coordinates = object.coordinates, i = -1, n = coordinates.length; + while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); + }, + GeometryCollection: function(object, listener) { + var geometries = object.geometries, i = -1, n = geometries.length; + while (++i < n) d3_geo_streamGeometry(geometries[i], listener); + } + }; + function d3_geo_streamLine(coordinates, listener, closed) { + var i = -1, n = coordinates.length - closed, coordinate; + listener.lineStart(); + while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]); + listener.lineEnd(); + } + function d3_geo_streamPolygon(coordinates, listener) { + var i = -1, n = coordinates.length; + listener.polygonStart(); + while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); + listener.polygonEnd(); + } + function d3_geo_spherical(cartesian) { + return [ Math.atan2(cartesian[1], cartesian[0]), Math.asin(Math.max(-1, Math.min(1, cartesian[2]))) ]; + } + function d3_geo_sphericalEqual(a, b) { + return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε; + } + function d3_geo_cartesian(spherical) { + var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); + return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; + } + function d3_geo_cartesianDot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; + } + function d3_geo_cartesianCross(a, b) { + return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; + } + function d3_geo_cartesianAdd(a, b) { + a[0] += b[0]; + a[1] += b[1]; + a[2] += b[2]; + } + function d3_geo_cartesianScale(vector, k) { + return [ vector[0] * k, vector[1] * k, vector[2] * k ]; + } + function d3_geo_cartesianNormalize(d) { + var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + d[0] /= l; + d[1] /= l; + d[2] /= l; + } + function d3_geo_resample(project) { + var δ2 = .5, maxDepth = 16; + function resample(stream) { + var λ0, x0, y0, a0, b0, c0; + var resample = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + stream.polygonStart(); + resample.lineStart = polygonLineStart; + }, + polygonEnd: function() { + stream.polygonEnd(); + resample.lineStart = lineStart; + } + }; + function point(x, y) { + x = project(x, y); + stream.point(x[0], x[1]); + } + function lineStart() { + x0 = NaN; + resample.point = linePoint; + stream.lineStart(); + } + function linePoint(λ, φ) { + var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); + resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); + stream.point(x0, y0); + } + function lineEnd() { + resample.point = point; + stream.lineEnd(); + } + function polygonLineStart() { + var λ00, φ00, x00, y00, a00, b00, c00; + lineStart(); + resample.point = function(λ, φ) { + linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; + resample.point = linePoint; + }; + resample.lineEnd = function() { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); + resample.lineEnd = lineEnd; + lineEnd(); + }; + } + return resample; + } + function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { + var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; + if (d2 > 4 * δ2 && depth--) { + var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = Math.abs(Math.abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; + if (dz * dz / d2 > δ2 || Math.abs((dx * dx2 + dy * dy2) / d2 - .5) > .3) { + resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); + stream.point(x2, y2); + resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); + } + } + } + resample.precision = function(_) { + if (!arguments.length) return Math.sqrt(δ2); + maxDepth = (δ2 = _ * _) > 0 && 16; + return resample; + }; + return resample; + } + d3.geo.albersUsa = function() { + var lower48 = d3.geo.albers(); + var alaska = d3.geo.albers().rotate([ 160, 0 ]).center([ 0, 60 ]).parallels([ 55, 65 ]); + var hawaii = d3.geo.albers().rotate([ 160, 0 ]).center([ 0, 20 ]).parallels([ 8, 18 ]); + var puertoRico = d3.geo.albers().rotate([ 60, 0 ]).center([ 0, 10 ]).parallels([ 8, 18 ]); + function albersUsa(coordinates) { + return projection(coordinates)(coordinates); + } + function projection(point) { + var lon = point[0], lat = point[1]; + return lat > 50 ? alaska : lon < -140 ? hawaii : lat < 21 ? puertoRico : lower48; + } + albersUsa.scale = function(x) { + if (!arguments.length) return lower48.scale(); + lower48.scale(x); + alaska.scale(x * .6); + hawaii.scale(x); + puertoRico.scale(x * 1.5); + return albersUsa.translate(lower48.translate()); + }; + albersUsa.translate = function(x) { + if (!arguments.length) return lower48.translate(); + var dz = lower48.scale(), dx = x[0], dy = x[1]; + lower48.translate(x); + alaska.translate([ dx - .4 * dz, dy + .17 * dz ]); + hawaii.translate([ dx - .19 * dz, dy + .2 * dz ]); + puertoRico.translate([ dx + .58 * dz, dy + .43 * dz ]); + return albersUsa; + }; + return albersUsa.scale(lower48.scale()); + }; + function d3_geo_albers(φ0, φ1) { + var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; + function albers(λ, φ) { + var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; + return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; + } + albers.invert = function(x, y) { + var ρ0_y = ρ0 - y; + return [ Math.atan2(x, ρ0_y) / n, Math.asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; + }; + return albers; + } + (d3.geo.albers = function() { + var φ0 = 29.5 * d3_radians, φ1 = 45.5 * d3_radians, m = d3_geo_projectionMutator(d3_geo_albers), p = m(φ0, φ1); + p.parallels = function(_) { + if (!arguments.length) return [ φ0 * d3_degrees, φ1 * d3_degrees ]; + return m(φ0 = _[0] * d3_radians, φ1 = _[1] * d3_radians); + }; + return p.rotate([ 98, 0 ]).center([ 0, 38 ]).scale(1e3); + }).raw = d3_geo_albers; + var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { + return Math.sqrt(2 / (1 + cosλcosφ)); + }, function(ρ) { + return 2 * Math.asin(ρ / 2); + }); + (d3.geo.azimuthalEqualArea = function() { + return d3_geo_projection(d3_geo_azimuthalEqualArea); + }).raw = d3_geo_azimuthalEqualArea; + var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { + var c = Math.acos(cosλcosφ); + return c && c / Math.sin(c); + }, d3_identity); + (d3.geo.azimuthalEquidistant = function() { + return d3_geo_projection(d3_geo_azimuthalEquidistant); + }).raw = d3_geo_azimuthalEquidistant; + d3.geo.bounds = d3_geo_bounds(d3_identity); + function d3_geo_bounds(projectStream) { + var x0, y0, x1, y1; + var bound = { + point: boundPoint, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + bound.lineEnd = boundPolygonLineEnd; + }, + polygonEnd: function() { + bound.point = boundPoint; + } + }; + function boundPoint(x, y) { + if (x < x0) x0 = x; + if (x > x1) x1 = x; + if (y < y0) y0 = y; + if (y > y1) y1 = y; + } + function boundPolygonLineEnd() { + bound.point = bound.lineEnd = d3_noop; + } + return function(feature) { + y1 = x1 = -(x0 = y0 = Infinity); + d3.geo.stream(feature, projectStream(bound)); + return [ [ x0, y0 ], [ x1, y1 ] ]; + }; + } + d3.geo.centroid = function(object) { + d3_geo_centroidDimension = d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + d3.geo.stream(object, d3_geo_centroid); + var m; + if (d3_geo_centroidW && Math.abs(m = Math.sqrt(d3_geo_centroidX * d3_geo_centroidX + d3_geo_centroidY * d3_geo_centroidY + d3_geo_centroidZ * d3_geo_centroidZ)) > ε) { + return [ Math.atan2(d3_geo_centroidY, d3_geo_centroidX) * d3_degrees, Math.asin(Math.max(-1, Math.min(1, d3_geo_centroidZ / m))) * d3_degrees ]; + } + }; + var d3_geo_centroidDimension, d3_geo_centroidW, d3_geo_centroidX, d3_geo_centroidY, d3_geo_centroidZ; + var d3_geo_centroid = { + sphere: function() { + if (d3_geo_centroidDimension < 2) { + d3_geo_centroidDimension = 2; + d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + }, + point: d3_geo_centroidPoint, + lineStart: d3_geo_centroidLineStart, + lineEnd: d3_geo_centroidLineEnd, + polygonStart: function() { + if (d3_geo_centroidDimension < 2) { + d3_geo_centroidDimension = 2; + d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + d3_geo_centroid.lineStart = d3_geo_centroidRingStart; + }, + polygonEnd: function() { + d3_geo_centroid.lineStart = d3_geo_centroidLineStart; + } + }; + function d3_geo_centroidPoint(λ, φ) { + if (d3_geo_centroidDimension) return; + ++d3_geo_centroidW; + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + d3_geo_centroidX += (cosφ * Math.cos(λ) - d3_geo_centroidX) / d3_geo_centroidW; + d3_geo_centroidY += (cosφ * Math.sin(λ) - d3_geo_centroidY) / d3_geo_centroidW; + d3_geo_centroidZ += (Math.sin(φ) - d3_geo_centroidZ) / d3_geo_centroidW; + } + function d3_geo_centroidRingStart() { + var λ00, φ00; + d3_geo_centroidDimension = 1; + d3_geo_centroidLineStart(); + d3_geo_centroidDimension = 2; + var linePoint = d3_geo_centroid.point; + d3_geo_centroid.point = function(λ, φ) { + linePoint(λ00 = λ, φ00 = φ); + }; + d3_geo_centroid.lineEnd = function() { + d3_geo_centroid.point(λ00, φ00); + d3_geo_centroidLineEnd(); + d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; + }; + } + function d3_geo_centroidLineStart() { + var x0, y0, z0; + if (d3_geo_centroidDimension > 1) return; + if (d3_geo_centroidDimension < 1) { + d3_geo_centroidDimension = 1; + d3_geo_centroidW = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + d3_geo_centroid.point = function(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians); + x0 = cosφ * Math.cos(λ); + y0 = cosφ * Math.sin(λ); + z0 = Math.sin(φ); + d3_geo_centroid.point = nextPoint; + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); + d3_geo_centroidW += w; + d3_geo_centroidX += w * (x0 + (x0 = x)); + d3_geo_centroidY += w * (y0 + (y0 = y)); + d3_geo_centroidZ += w * (z0 + (z0 = z)); + } + } + function d3_geo_centroidLineEnd() { + d3_geo_centroid.point = d3_geo_centroidPoint; + } + d3.geo.circle = function() { + var origin = [ 0, 0 ], angle, precision = 6, interpolate; + function circle() { + var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; + interpolate(null, null, 1, { + point: function(x, y) { + ring.push(x = rotate(x, y)); + x[0] *= d3_degrees, x[1] *= d3_degrees; + } + }); + return { + type: "Polygon", + coordinates: [ ring ] + }; + } + circle.origin = function(x) { + if (!arguments.length) return origin; + origin = x; + return circle; + }; + circle.angle = function(x) { + if (!arguments.length) return angle; + interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); + return circle; + }; + circle.precision = function(_) { + if (!arguments.length) return precision; + interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); + return circle; + }; + return circle.angle(90); + }; + function d3_geo_circleInterpolate(radians, precision) { + var cr = Math.cos(radians), sr = Math.sin(radians); + return function(from, to, direction, listener) { + if (from != null) { + from = d3_geo_circleAngle(cr, from); + to = d3_geo_circleAngle(cr, to); + if (direction > 0 ? from < to : from > to) from += direction * 2 * π; + } else { + from = radians + direction * 2 * π; + to = radians; + } + var point; + for (var step = direction * precision, t = from; direction > 0 ? t > to : t < to; t -= step) { + listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); + } + }; + } + function d3_geo_circleAngle(cr, point) { + var a = d3_geo_cartesian(point); + a[0] -= cr; + d3_geo_cartesianNormalize(a); + var angle = Math.acos(Math.max(-1, Math.min(1, -a[1]))); + return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); + } + function d3_geo_clip(pointVisible, clipLine, interpolate) { + return function(listener) { + var line = clipLine(listener); + var clip = { + point: point, + lineStart: lineStart, + lineEnd: lineEnd, + polygonStart: function() { + clip.point = pointRing; + clip.lineStart = ringStart; + clip.lineEnd = ringEnd; + invisible = false; + invisibleArea = visibleArea = 0; + segments = []; + listener.polygonStart(); + }, + polygonEnd: function() { + clip.point = point; + clip.lineStart = lineStart; + clip.lineEnd = lineEnd; + segments = d3.merge(segments); + if (segments.length) { + d3_geo_clipPolygon(segments, interpolate, listener); + } else if (visibleArea < -ε || invisible && invisibleArea < -ε) { + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + } + listener.polygonEnd(); + segments = null; + }, + sphere: function() { + listener.polygonStart(); + listener.lineStart(); + interpolate(null, null, 1, listener); + listener.lineEnd(); + listener.polygonEnd(); + } + }; + function point(λ, φ) { + if (pointVisible(λ, φ)) listener.point(λ, φ); + } + function pointLine(λ, φ) { + line.point(λ, φ); + } + function lineStart() { + clip.point = pointLine; + line.lineStart(); + } + function lineEnd() { + clip.point = point; + line.lineEnd(); + } + var segments, visibleArea, invisibleArea, invisible; + var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), ring; + function pointRing(λ, φ) { + ringListener.point(λ, φ); + ring.push([ λ, φ ]); + } + function ringStart() { + ringListener.lineStart(); + ring = []; + } + function ringEnd() { + pointRing(ring[0][0], ring[0][1]); + ringListener.lineEnd(); + var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; + if (!n) { + invisible = true; + invisibleArea += d3_geo_clipAreaRing(ring, -1); + ring = null; + return; + } + ring = null; + if (clean & 1) { + segment = ringSegments[0]; + visibleArea += d3_geo_clipAreaRing(segment, 1); + var n = segment.length - 1, i = -1, point; + listener.lineStart(); + while (++i < n) listener.point((point = segment[i])[0], point[1]); + listener.lineEnd(); + return; + } + if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); + segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); + } + return clip; + }; + } + function d3_geo_clipPolygon(segments, interpolate, listener) { + var subject = [], clip = []; + segments.forEach(function(segment) { + var n = segment.length; + if (n <= 1) return; + var p0 = segment[0], p1 = segment[n - 1], a = { + point: p0, + points: segment, + other: null, + visited: false, + entry: true, + subject: true + }, b = { + point: p0, + points: [ p0 ], + other: a, + visited: false, + entry: false, + subject: false + }; + a.other = b; + subject.push(a); + clip.push(b); + a = { + point: p1, + points: [ p1 ], + other: null, + visited: false, + entry: false, + subject: true + }; + b = { + point: p1, + points: [ p1 ], + other: a, + visited: false, + entry: true, + subject: false + }; + a.other = b; + subject.push(a); + clip.push(b); + }); + clip.sort(d3_geo_clipSort); + d3_geo_clipLinkCircular(subject); + d3_geo_clipLinkCircular(clip); + if (!subject.length) return; + var start = subject[0], current, points, point; + while (1) { + current = start; + while (current.visited) if ((current = current.next) === start) return; + points = current.points; + listener.lineStart(); + do { + current.visited = current.other.visited = true; + if (current.entry) { + if (current.subject) { + for (var i = 0; i < points.length; i++) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.point, current.next.point, 1, listener); + } + current = current.next; + } else { + if (current.subject) { + points = current.prev.points; + for (var i = points.length; --i >= 0; ) listener.point((point = points[i])[0], point[1]); + } else { + interpolate(current.point, current.prev.point, -1, listener); + } + current = current.prev; + } + current = current.other; + points = current.points; + } while (!current.visited); + listener.lineEnd(); + } + } + function d3_geo_clipLinkCircular(array) { + if (!(n = array.length)) return; + var n, i = 0, a = array[0], b; + while (++i < n) { + a.next = b = array[i]; + b.prev = a; + a = b; + } + a.next = b = array[0]; + b.prev = a; + } + function d3_geo_clipSort(a, b) { + return ((a = a.point)[0] < 0 ? a[1] - π / 2 - ε : π / 2 - a[1]) - ((b = b.point)[0] < 0 ? b[1] - π / 2 - ε : π / 2 - b[1]); + } + function d3_geo_clipSegmentLength1(segment) { + return segment.length > 1; + } + function d3_geo_clipBufferListener() { + var lines = [], line; + return { + lineStart: function() { + lines.push(line = []); + }, + point: function(λ, φ) { + line.push([ λ, φ ]); + }, + lineEnd: d3_noop, + buffer: function() { + var buffer = lines; + lines = []; + line = null; + return buffer; + } + }; + } + function d3_geo_clipAreaRing(ring, invisible) { + if (!(n = ring.length)) return 0; + var n, i = 0, area = 0, p = ring[0], λ = p[0], φ = p[1], cosφ = Math.cos(φ), x0 = Math.atan2(invisible * Math.sin(λ) * cosφ, Math.sin(φ)), y0 = 1 - invisible * Math.cos(λ) * cosφ, x1 = x0, x, y; + while (++i < n) { + p = ring[i]; + cosφ = Math.cos(φ = p[1]); + x = Math.atan2(invisible * Math.sin(λ = p[0]) * cosφ, Math.sin(φ)); + y = 1 - invisible * Math.cos(λ) * cosφ; + if (Math.abs(y0 - 2) < ε && Math.abs(y - 2) < ε) continue; + if (Math.abs(y) < ε || Math.abs(y0) < ε) {} else if (Math.abs(Math.abs(x - x0) - π) < ε) { + if (y + y0 > 2) area += 4 * (x - x0); + } else if (Math.abs(y0 - 2) < ε) area += 4 * (x - x1); else area += ((3 * π + x - x0) % (2 * π) - π) * (y0 + y); + x1 = x0, x0 = x, y0 = y; + } + return area; + } + var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate); + function d3_geo_clipAntimeridianLine(listener) { + var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; + return { + lineStart: function() { + listener.lineStart(); + clean = 1; + }, + point: function(λ1, φ1) { + var sλ1 = λ1 > 0 ? π : -π, dλ = Math.abs(λ1 - λ0); + if (Math.abs(dλ - π) < ε) { + listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? π / 2 : -π / 2); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + listener.point(λ1, φ0); + clean = 0; + } else if (sλ0 !== sλ1 && dλ >= π) { + if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; + if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; + φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); + listener.point(sλ0, φ0); + listener.lineEnd(); + listener.lineStart(); + listener.point(sλ1, φ0); + clean = 0; + } + listener.point(λ0 = λ1, φ0 = φ1); + sλ0 = sλ1; + }, + lineEnd: function() { + listener.lineEnd(); + λ0 = φ0 = NaN; + }, + clean: function() { + return 2 - clean; + } + }; + } + function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { + var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); + return Math.abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; + } + function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { + var φ; + if (from == null) { + φ = direction * π / 2; + listener.point(-π, φ); + listener.point(0, φ); + listener.point(π, φ); + listener.point(π, 0); + listener.point(π, -φ); + listener.point(0, -φ); + listener.point(-π, -φ); + listener.point(-π, 0); + listener.point(-π, φ); + } else if (Math.abs(from[0] - to[0]) > ε) { + var s = (from[0] < to[0] ? 1 : -1) * π; + φ = direction * s / 2; + listener.point(-s, φ); + listener.point(0, φ); + listener.point(s, φ); + } else { + listener.point(to[0], to[1]); + } + } + function d3_geo_clipCircle(degrees) { + var radians = degrees * d3_radians, cr = Math.cos(radians), interpolate = d3_geo_circleInterpolate(radians, 6 * d3_radians); + return d3_geo_clip(visible, clipLine, interpolate); + function visible(λ, φ) { + return Math.cos(λ) * Math.cos(φ) > cr; + } + function clipLine(listener) { + var point0, v0, v00, clean; + return { + lineStart: function() { + v00 = v0 = false; + clean = 1; + }, + point: function(λ, φ) { + var point1 = [ λ, φ ], point2, v = visible(λ, φ); + if (!point0 && (v00 = v0 = v)) listener.lineStart(); + if (v !== v0) { + point2 = intersect(point0, point1); + if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { + point1[0] += ε; + point1[1] += ε; + v = visible(point1[0], point1[1]); + } + } + if (v !== v0) { + clean = 0; + if (v0 = v) { + listener.lineStart(); + point2 = intersect(point1, point0); + listener.point(point2[0], point2[1]); + } else { + point2 = intersect(point0, point1); + listener.point(point2[0], point2[1]); + listener.lineEnd(); + } + point0 = point2; + } + if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) listener.point(point1[0], point1[1]); + point0 = point1; + }, + lineEnd: function() { + if (v0) listener.lineEnd(); + point0 = null; + }, + clean: function() { + return clean | (v00 && v0) << 1; + } + }; + } + function intersect(a, b) { + var pa = d3_geo_cartesian(a, 0), pb = d3_geo_cartesian(b, 0); + var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; + if (!determinant) return a; + var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); + d3_geo_cartesianAdd(A, B); + var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t = Math.sqrt(w * w - uu * (d3_geo_cartesianDot(A, A) - 1)), q = d3_geo_cartesianScale(u, (-w - t) / uu); + d3_geo_cartesianAdd(q, A); + return d3_geo_spherical(q); + } + } + function d3_geo_compose(a, b) { + function compose(x, y) { + return x = a(x, y), b(x[0], x[1]); + } + if (a.invert && b.invert) compose.invert = function(x, y) { + return x = b.invert(x, y), x && a.invert(x[0], x[1]); + }; + return compose; + } + function d3_geo_equirectangular(λ, φ) { + return [ λ, φ ]; + } + (d3.geo.equirectangular = function() { + return d3_geo_projection(d3_geo_equirectangular).scale(250 / π); + }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; + var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / cosλcosφ; + }, Math.atan); + (d3.geo.gnomonic = function() { + return d3_geo_projection(d3_geo_gnomonic); + }).raw = d3_geo_gnomonic; + d3.geo.graticule = function() { + var x1, x0, y1, y0, dx = 22.5, dy = dx, x, y, precision = 2.5; + function graticule() { + return { + type: "MultiLineString", + coordinates: lines() + }; + } + function lines() { + return d3.range(Math.ceil(x0 / dx) * dx, x1, dx).map(x).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).map(y)); + } + graticule.lines = function() { + return lines().map(function(coordinates) { + return { + type: "LineString", + coordinates: coordinates + }; + }); + }; + graticule.outline = function() { + return { + type: "Polygon", + coordinates: [ x(x0).concat(y(y1).slice(1), x(x1).reverse().slice(1), y(y0).reverse().slice(1)) ] + }; + }; + graticule.extent = function(_) { + if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; + x0 = +_[0][0], x1 = +_[1][0]; + y0 = +_[0][1], y1 = +_[1][1]; + if (x0 > x1) _ = x0, x0 = x1, x1 = _; + if (y0 > y1) _ = y0, y0 = y1, y1 = _; + return graticule.precision(precision); + }; + graticule.step = function(_) { + if (!arguments.length) return [ dx, dy ]; + dx = +_[0], dy = +_[1]; + return graticule; + }; + graticule.precision = function(_) { + if (!arguments.length) return precision; + precision = +_; + x = d3_geo_graticuleX(y0, y1, precision); + y = d3_geo_graticuleY(x0, x1, precision); + return graticule; + }; + return graticule.extent([ [ -180 + ε, -90 + ε ], [ 180 - ε, 90 - ε ] ]); + }; + function d3_geo_graticuleX(y0, y1, dy) { + var y = d3.range(y0, y1 - ε, dy).concat(y1); + return function(x) { + return y.map(function(y) { + return [ x, y ]; + }); + }; + } + function d3_geo_graticuleY(x0, x1, dx) { + var x = d3.range(x0, x1 - ε, dx).concat(x1); + return function(y) { + return x.map(function(x) { + return [ x, y ]; + }); + }; + } + d3.geo.interpolate = function(source, target) { + return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); + }; + function d3_geo_interpolate(x0, y0, x1, y1) { + var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = Math.acos(Math.max(-1, Math.min(1, sy0 * sy1 + cy0 * cy1 * Math.cos(x1 - x0)))), k = 1 / Math.sin(d); + function interpolate(t) { + var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; + return [ Math.atan2(y, x) / d3_radians, Math.atan2(z, Math.sqrt(x * x + y * y)) / d3_radians ]; + } + interpolate.distance = d; + return interpolate; + } + d3.geo.greatArc = function() { + var source = d3_source, source_, target = d3_target, target_, precision = 6 * d3_radians, interpolate; + function greatArc() { + var p0 = source_ || source.apply(this, arguments), p1 = target_ || target.apply(this, arguments), i = interpolate || d3.geo.interpolate(p0, p1), t = 0, dt = precision / i.distance, coordinates = [ p0 ]; + while ((t += dt) < 1) coordinates.push(i(t)); + coordinates.push(p1); + return { + type: "LineString", + coordinates: coordinates + }; + } + greatArc.distance = function() { + return (interpolate || d3.geo.interpolate(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments))).distance; + }; + greatArc.source = function(_) { + if (!arguments.length) return source; + source = _, source_ = typeof _ === "function" ? null : _; + interpolate = source_ && target_ ? d3.geo.interpolate(source_, target_) : null; + return greatArc; + }; + greatArc.target = function(_) { + if (!arguments.length) return target; + target = _, target_ = typeof _ === "function" ? null : _; + interpolate = source_ && target_ ? d3.geo.interpolate(source_, target_) : null; + return greatArc; + }; + greatArc.precision = function(_) { + if (!arguments.length) return precision / d3_radians; + precision = _ * d3_radians; + return greatArc; + }; + return greatArc; + }; + function d3_geo_mercator(λ, φ) { + return [ λ / (2 * π), Math.max(-.5, Math.min(+.5, Math.log(Math.tan(π / 4 + φ / 2)) / (2 * π))) ]; + } + d3_geo_mercator.invert = function(x, y) { + return [ 2 * π * x, 2 * Math.atan(Math.exp(2 * π * y)) - π / 2 ]; + }; + (d3.geo.mercator = function() { + return d3_geo_projection(d3_geo_mercator).scale(500); + }).raw = d3_geo_mercator; + var d3_geo_orthographic = d3_geo_azimuthal(function() { + return 1; + }, Math.asin); + (d3.geo.orthographic = function() { + return d3_geo_projection(d3_geo_orthographic); + }).raw = d3_geo_orthographic; + d3.geo.path = function() { + var pointRadius = 4.5, projection, context, projectStream, contextStream; + function path(object) { + if (object) d3.geo.stream(object, projectStream(contextStream.pointRadius(typeof pointRadius === "function" ? +pointRadius.apply(this, arguments) : pointRadius))); + return contextStream.result(); + } + path.area = function(object) { + d3_geo_pathAreaSum = 0; + d3.geo.stream(object, projectStream(d3_geo_pathArea)); + return d3_geo_pathAreaSum; + }; + path.centroid = function(object) { + d3_geo_centroidDimension = d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); + return d3_geo_centroidZ ? [ d3_geo_centroidX / d3_geo_centroidZ, d3_geo_centroidY / d3_geo_centroidZ ] : undefined; + }; + path.bounds = function(object) { + return d3_geo_bounds(projectStream)(object); + }; + path.projection = function(_) { + if (!arguments.length) return projection; + projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; + return path; + }; + path.context = function(_) { + if (!arguments.length) return context; + contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); + return path; + }; + path.pointRadius = function(_) { + if (!arguments.length) return pointRadius; + pointRadius = typeof _ === "function" ? _ : +_; + return path; + }; + return path.projection(d3.geo.albersUsa()).context(null); + }; + function d3_geo_pathCircle(radius) { + return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + +2 * radius + "z"; + } + function d3_geo_pathProjectStream(project) { + var resample = d3_geo_resample(function(λ, φ) { + return project([ λ * d3_degrees, φ * d3_degrees ]); + }); + return function(stream) { + stream = resample(stream); + return { + point: function(λ, φ) { + stream.point(λ * d3_radians, φ * d3_radians); + }, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + }; + } + function d3_geo_pathBuffer() { + var pointCircle = d3_geo_pathCircle(4.5), buffer = []; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointCircle = d3_geo_pathCircle(_); + return stream; + }, + result: function() { + if (buffer.length) { + var result = buffer.join(""); + buffer = []; + return result; + } + } + }; + function point(x, y) { + buffer.push("M", x, ",", y, pointCircle); + } + function pointLineStart(x, y) { + buffer.push("M", x, ",", y); + stream.point = pointLine; + } + function pointLine(x, y) { + buffer.push("L", x, ",", y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + buffer.push("Z"); + } + return stream; + } + function d3_geo_pathContext(context) { + var pointRadius = 4.5; + var stream = { + point: point, + lineStart: function() { + stream.point = pointLineStart; + }, + lineEnd: lineEnd, + polygonStart: function() { + stream.lineEnd = lineEndPolygon; + }, + polygonEnd: function() { + stream.lineEnd = lineEnd; + stream.point = point; + }, + pointRadius: function(_) { + pointRadius = _; + return stream; + }, + result: d3_noop + }; + function point(x, y) { + context.moveTo(x, y); + context.arc(x, y, pointRadius, 0, 2 * π); + } + function pointLineStart(x, y) { + context.moveTo(x, y); + stream.point = pointLine; + } + function pointLine(x, y) { + context.lineTo(x, y); + } + function lineEnd() { + stream.point = point; + } + function lineEndPolygon() { + context.closePath(); + } + return stream; + } + var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_pathAreaPolygon = 0; + d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; + }, + polygonEnd: function() { + d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; + d3_geo_pathAreaSum += Math.abs(d3_geo_pathAreaPolygon / 2); + } + }; + function d3_geo_pathAreaRingStart() { + var x00, y00, x0, y0; + d3_geo_pathArea.point = function(x, y) { + d3_geo_pathArea.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + d3_geo_pathAreaPolygon += y0 * x - x0 * y; + x0 = x, y0 = y; + } + d3_geo_pathArea.lineEnd = function() { + nextPoint(x00, y00); + }; + } + var d3_geo_pathCentroid = { + point: d3_geo_pathCentroidPoint, + lineStart: d3_geo_pathCentroidLineStart, + lineEnd: d3_geo_pathCentroidLineEnd, + polygonStart: function() { + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; + }, + polygonEnd: function() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; + d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; + } + }; + function d3_geo_pathCentroidPoint(x, y) { + if (d3_geo_centroidDimension) return; + d3_geo_centroidX += x; + d3_geo_centroidY += y; + ++d3_geo_centroidZ; + } + function d3_geo_pathCentroidLineStart() { + var x0, y0; + if (d3_geo_centroidDimension !== 1) { + if (d3_geo_centroidDimension < 1) { + d3_geo_centroidDimension = 1; + d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } else return; + } + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + x0 = x, y0 = y; + }; + function nextPoint(x, y) { + var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); + d3_geo_centroidX += z * (x0 + x) / 2; + d3_geo_centroidY += z * (y0 + y) / 2; + d3_geo_centroidZ += z; + x0 = x, y0 = y; + } + } + function d3_geo_pathCentroidLineEnd() { + d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; + } + function d3_geo_pathCentroidRingStart() { + var x00, y00, x0, y0; + if (d3_geo_centroidDimension < 2) { + d3_geo_centroidDimension = 2; + d3_geo_centroidX = d3_geo_centroidY = d3_geo_centroidZ = 0; + } + d3_geo_pathCentroid.point = function(x, y) { + d3_geo_pathCentroid.point = nextPoint; + x00 = x0 = x, y00 = y0 = y; + }; + function nextPoint(x, y) { + var z = y0 * x - x0 * y; + d3_geo_centroidX += z * (x0 + x); + d3_geo_centroidY += z * (y0 + y); + d3_geo_centroidZ += z * 3; + x0 = x, y0 = y; + } + d3_geo_pathCentroid.lineEnd = function() { + nextPoint(x00, y00); + }; + } + d3.geo.area = function(object) { + d3_geo_areaSum = 0; + d3.geo.stream(object, d3_geo_area); + return d3_geo_areaSum; + }; + var d3_geo_areaSum, d3_geo_areaRingU, d3_geo_areaRingV; + var d3_geo_area = { + sphere: function() { + d3_geo_areaSum += 4 * π; + }, + point: d3_noop, + lineStart: d3_noop, + lineEnd: d3_noop, + polygonStart: function() { + d3_geo_areaRingU = 1, d3_geo_areaRingV = 0; + d3_geo_area.lineStart = d3_geo_areaRingStart; + }, + polygonEnd: function() { + var area = 2 * Math.atan2(d3_geo_areaRingV, d3_geo_areaRingU); + d3_geo_areaSum += area < 0 ? 4 * π + area : area; + d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; + } + }; + function d3_geo_areaRingStart() { + var λ00, φ00, λ0, cosφ0, sinφ0; + d3_geo_area.point = function(λ, φ) { + d3_geo_area.point = nextPoint; + λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), + sinφ0 = Math.sin(φ); + }; + function nextPoint(λ, φ) { + λ *= d3_radians; + φ = φ * d3_radians / 2 + π / 4; + var dλ = λ - λ0, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u0 = d3_geo_areaRingU, v0 = d3_geo_areaRingV, u = cosφ0 * cosφ + k * Math.cos(dλ), v = k * Math.sin(dλ); + d3_geo_areaRingU = u0 * u - v0 * v; + d3_geo_areaRingV = v0 * u + u0 * v; + λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; + } + d3_geo_area.lineEnd = function() { + nextPoint(λ00, φ00); + }; + } + d3.geo.projection = d3_geo_projection; + d3.geo.projectionMutator = d3_geo_projectionMutator; + function d3_geo_projection(project) { + return d3_geo_projectionMutator(function() { + return project; + })(); + } + function d3_geo_projectionMutator(projectAt) { + var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { + x = project(x, y); + return [ x[0] * k + δx, δy - x[1] * k ]; + }), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, clip = d3_geo_clipAntimeridian, clipAngle = null; + function projection(point) { + point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); + return [ point[0] * k + δx, δy - point[1] * k ]; + } + function invert(point) { + point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); + return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; + } + projection.stream = function(stream) { + return d3_geo_projectionRadiansRotate(rotate, clip(projectResample(stream))); + }; + projection.clipAngle = function(_) { + if (!arguments.length) return clipAngle; + clip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle(clipAngle = +_); + return projection; + }; + projection.scale = function(_) { + if (!arguments.length) return k; + k = +_; + return reset(); + }; + projection.translate = function(_) { + if (!arguments.length) return [ x, y ]; + x = +_[0]; + y = +_[1]; + return reset(); + }; + projection.center = function(_) { + if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; + λ = _[0] % 360 * d3_radians; + φ = _[1] % 360 * d3_radians; + return reset(); + }; + projection.rotate = function(_) { + if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; + δλ = _[0] % 360 * d3_radians; + δφ = _[1] % 360 * d3_radians; + δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; + return reset(); + }; + d3.rebind(projection, projectResample, "precision"); + function reset() { + projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); + var center = project(λ, φ); + δx = x - center[0] * k; + δy = y + center[1] * k; + return projection; + } + return function() { + project = projectAt.apply(this, arguments); + projection.invert = project.invert && invert; + return reset(); + }; + } + function d3_geo_projectionRadiansRotate(rotate, stream) { + return { + point: function(x, y) { + y = rotate(x * d3_radians, y * d3_radians), x = y[0]; + stream.point(x > π ? x - 2 * π : x < -π ? x + 2 * π : x, y[1]); + }, + sphere: function() { + stream.sphere(); + }, + lineStart: function() { + stream.lineStart(); + }, + lineEnd: function() { + stream.lineEnd(); + }, + polygonStart: function() { + stream.polygonStart(); + }, + polygonEnd: function() { + stream.polygonEnd(); + } + }; + } + function d3_geo_rotation(δλ, δφ, δγ) { + return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_equirectangular; + } + function d3_geo_forwardRotationλ(δλ) { + return function(λ, φ) { + return λ += δλ, [ λ > π ? λ - 2 * π : λ < -π ? λ + 2 * π : λ, φ ]; + }; + } + function d3_geo_rotationλ(δλ) { + var rotation = d3_geo_forwardRotationλ(δλ); + rotation.invert = d3_geo_forwardRotationλ(-δλ); + return rotation; + } + function d3_geo_rotationφγ(δφ, δγ) { + var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); + function rotation(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; + return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), Math.asin(Math.max(-1, Math.min(1, k * cosδγ + y * sinδγ))) ]; + } + rotation.invert = function(λ, φ) { + var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; + return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), Math.asin(Math.max(-1, Math.min(1, k * cosδφ - x * sinδφ))) ]; + }; + return rotation; + } + var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { + return 1 / (1 + cosλcosφ); + }, function(ρ) { + return 2 * Math.atan(ρ); + }); + (d3.geo.stereographic = function() { + return d3_geo_projection(d3_geo_stereographic); + }).raw = d3_geo_stereographic; + function d3_geo_azimuthal(scale, angle) { + function azimuthal(λ, φ) { + var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); + return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; + } + azimuthal.invert = function(x, y) { + var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); + return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; + }; + return azimuthal; + } + d3.geom = {}; + d3.geom.hull = function(vertices) { + if (vertices.length < 3) return []; + var len = vertices.length, plen = len - 1, points = [], stack = [], i, j, h = 0, x1, y1, x2, y2, u, v, a, sp; + for (i = 1; i < len; ++i) { + if (vertices[i][1] < vertices[h][1]) { + h = i; + } else if (vertices[i][1] == vertices[h][1]) { + h = vertices[i][0] < vertices[h][0] ? i : h; + } + } + for (i = 0; i < len; ++i) { + if (i === h) continue; + y1 = vertices[i][1] - vertices[h][1]; + x1 = vertices[i][0] - vertices[h][0]; + points.push({ + angle: Math.atan2(y1, x1), + index: i + }); + } + points.sort(function(a, b) { + return a.angle - b.angle; + }); + a = points[0].angle; + v = points[0].index; + u = 0; + for (i = 1; i < plen; ++i) { + j = points[i].index; + if (a == points[i].angle) { + x1 = vertices[v][0] - vertices[h][0]; + y1 = vertices[v][1] - vertices[h][1]; + x2 = vertices[j][0] - vertices[h][0]; + y2 = vertices[j][1] - vertices[h][1]; + if (x1 * x1 + y1 * y1 >= x2 * x2 + y2 * y2) { + points[i].index = -1; + } else { + points[u].index = -1; + a = points[i].angle; + u = i; + v = j; + } + } else { + a = points[i].angle; + u = i; + v = j; + } + } + stack.push(h); + for (i = 0, j = 0; i < 2; ++j) { + if (points[j].index !== -1) { + stack.push(points[j].index); + i++; + } + } + sp = stack.length; + for (;j < plen; ++j) { + if (points[j].index === -1) continue; + while (!d3_geom_hullCCW(stack[sp - 2], stack[sp - 1], points[j].index, vertices)) { + --sp; + } + stack[sp++] = points[j].index; + } + var poly = []; + for (i = 0; i < sp; ++i) { + poly.push(vertices[stack[i]]); + } + return poly; + }; + function d3_geom_hullCCW(i1, i2, i3, v) { + var t, a, b, c, d, e, f; + t = v[i1]; + a = t[0]; + b = t[1]; + t = v[i2]; + c = t[0]; + d = t[1]; + t = v[i3]; + e = t[0]; + f = t[1]; + return (f - b) * (c - a) - (d - b) * (e - a) > 0; + } + d3.geom.polygon = function(coordinates) { + coordinates.area = function() { + var i = 0, n = coordinates.length, area = coordinates[n - 1][1] * coordinates[0][0] - coordinates[n - 1][0] * coordinates[0][1]; + while (++i < n) { + area += coordinates[i - 1][1] * coordinates[i][0] - coordinates[i - 1][0] * coordinates[i][1]; + } + return area * .5; + }; + coordinates.centroid = function(k) { + var i = -1, n = coordinates.length, x = 0, y = 0, a, b = coordinates[n - 1], c; + if (!arguments.length) k = -1 / (6 * coordinates.area()); + while (++i < n) { + a = b; + b = coordinates[i]; + c = a[0] * b[1] - b[0] * a[1]; + x += (a[0] + b[0]) * c; + y += (a[1] + b[1]) * c; + } + return [ x * k, y * k ]; + }; + coordinates.clip = function(subject) { + var input, i = -1, n = coordinates.length, j, m, a = coordinates[n - 1], b, c, d; + while (++i < n) { + input = subject.slice(); + subject.length = 0; + b = coordinates[i]; + c = input[(m = input.length) - 1]; + j = -1; + while (++j < m) { + d = input[j]; + if (d3_geom_polygonInside(d, a, b)) { + if (!d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + subject.push(d); + } else if (d3_geom_polygonInside(c, a, b)) { + subject.push(d3_geom_polygonIntersect(c, d, a, b)); + } + c = d; + } + a = b; + } + return subject; + }; + return coordinates; + }; + function d3_geom_polygonInside(p, a, b) { + return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); + } + function d3_geom_polygonIntersect(c, d, a, b) { + var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); + return [ x1 + ua * x21, y1 + ua * y21 ]; + } + d3.geom.voronoi = function(vertices) { + var polygons = vertices.map(function() { + return []; + }), Z = 1e6; + d3_voronoi_tessellate(vertices, function(e) { + var s1, s2, x1, x2, y1, y2; + if (e.a === 1 && e.b >= 0) { + s1 = e.ep.r; + s2 = e.ep.l; + } else { + s1 = e.ep.l; + s2 = e.ep.r; + } + if (e.a === 1) { + y1 = s1 ? s1.y : -Z; + x1 = e.c - e.b * y1; + y2 = s2 ? s2.y : Z; + x2 = e.c - e.b * y2; + } else { + x1 = s1 ? s1.x : -Z; + y1 = e.c - e.a * x1; + x2 = s2 ? s2.x : Z; + y2 = e.c - e.a * x2; + } + var v1 = [ x1, y1 ], v2 = [ x2, y2 ]; + polygons[e.region.l.index].push(v1, v2); + polygons[e.region.r.index].push(v1, v2); + }); + polygons = polygons.map(function(polygon, i) { + var cx = vertices[i][0], cy = vertices[i][1], angle = polygon.map(function(v) { + return Math.atan2(v[0] - cx, v[1] - cy); + }), order = d3.range(polygon.length).sort(function(a, b) { + return angle[a] - angle[b]; + }); + return order.filter(function(d, i) { + return !i || angle[d] - angle[order[i - 1]] > ε; + }).map(function(d) { + return polygon[d]; + }); + }); + polygons.forEach(function(polygon, i) { + var n = polygon.length; + if (!n) return polygon.push([ -Z, -Z ], [ -Z, Z ], [ Z, Z ], [ Z, -Z ]); + if (n > 2) return; + var p0 = vertices[i], p1 = polygon[0], p2 = polygon[1], x0 = p0[0], y0 = p0[1], x1 = p1[0], y1 = p1[1], x2 = p2[0], y2 = p2[1], dx = Math.abs(x2 - x1), dy = y2 - y1; + if (Math.abs(dy) < ε) { + var y = y0 < y1 ? -Z : Z; + polygon.push([ -Z, y ], [ Z, y ]); + } else if (dx < ε) { + var x = x0 < x1 ? -Z : Z; + polygon.push([ x, -Z ], [ x, Z ]); + } else { + var y = (x2 - x1) * (y1 - y0) < (x1 - x0) * (y2 - y1) ? Z : -Z, z = Math.abs(dy) - dx; + if (Math.abs(z) < ε) { + polygon.push([ dy < 0 ? y : -y, y ]); + } else { + if (z > 0) y *= -1; + polygon.push([ -Z, y ], [ Z, y ]); + } + } + }); + return polygons; + }; + var d3_voronoi_opposite = { + l: "r", + r: "l" + }; + function d3_voronoi_tessellate(vertices, callback) { + var Sites = { + list: vertices.map(function(v, i) { + return { + index: i, + x: v[0], + y: v[1] + }; + }).sort(function(a, b) { + return a.y < b.y ? -1 : a.y > b.y ? 1 : a.x < b.x ? -1 : a.x > b.x ? 1 : 0; + }), + bottomSite: null + }; + var EdgeList = { + list: [], + leftEnd: null, + rightEnd: null, + init: function() { + EdgeList.leftEnd = EdgeList.createHalfEdge(null, "l"); + EdgeList.rightEnd = EdgeList.createHalfEdge(null, "l"); + EdgeList.leftEnd.r = EdgeList.rightEnd; + EdgeList.rightEnd.l = EdgeList.leftEnd; + EdgeList.list.unshift(EdgeList.leftEnd, EdgeList.rightEnd); + }, + createHalfEdge: function(edge, side) { + return { + edge: edge, + side: side, + vertex: null, + l: null, + r: null + }; + }, + insert: function(lb, he) { + he.l = lb; + he.r = lb.r; + lb.r.l = he; + lb.r = he; + }, + leftBound: function(p) { + var he = EdgeList.leftEnd; + do { + he = he.r; + } while (he != EdgeList.rightEnd && Geom.rightOf(he, p)); + he = he.l; + return he; + }, + del: function(he) { + he.l.r = he.r; + he.r.l = he.l; + he.edge = null; + }, + right: function(he) { + return he.r; + }, + left: function(he) { + return he.l; + }, + leftRegion: function(he) { + return he.edge == null ? Sites.bottomSite : he.edge.region[he.side]; + }, + rightRegion: function(he) { + return he.edge == null ? Sites.bottomSite : he.edge.region[d3_voronoi_opposite[he.side]]; + } + }; + var Geom = { + bisect: function(s1, s2) { + var newEdge = { + region: { + l: s1, + r: s2 + }, + ep: { + l: null, + r: null + } + }; + var dx = s2.x - s1.x, dy = s2.y - s1.y, adx = dx > 0 ? dx : -dx, ady = dy > 0 ? dy : -dy; + newEdge.c = s1.x * dx + s1.y * dy + (dx * dx + dy * dy) * .5; + if (adx > ady) { + newEdge.a = 1; + newEdge.b = dy / dx; + newEdge.c /= dx; + } else { + newEdge.b = 1; + newEdge.a = dx / dy; + newEdge.c /= dy; + } + return newEdge; + }, + intersect: function(el1, el2) { + var e1 = el1.edge, e2 = el2.edge; + if (!e1 || !e2 || e1.region.r == e2.region.r) { + return null; + } + var d = e1.a * e2.b - e1.b * e2.a; + if (Math.abs(d) < 1e-10) { + return null; + } + var xint = (e1.c * e2.b - e2.c * e1.b) / d, yint = (e2.c * e1.a - e1.c * e2.a) / d, e1r = e1.region.r, e2r = e2.region.r, el, e; + if (e1r.y < e2r.y || e1r.y == e2r.y && e1r.x < e2r.x) { + el = el1; + e = e1; + } else { + el = el2; + e = e2; + } + var rightOfSite = xint >= e.region.r.x; + if (rightOfSite && el.side === "l" || !rightOfSite && el.side === "r") { + return null; + } + return { + x: xint, + y: yint + }; + }, + rightOf: function(he, p) { + var e = he.edge, topsite = e.region.r, rightOfSite = p.x > topsite.x; + if (rightOfSite && he.side === "l") { + return 1; + } + if (!rightOfSite && he.side === "r") { + return 0; + } + if (e.a === 1) { + var dyp = p.y - topsite.y, dxp = p.x - topsite.x, fast = 0, above = 0; + if (!rightOfSite && e.b < 0 || rightOfSite && e.b >= 0) { + above = fast = dyp >= e.b * dxp; + } else { + above = p.x + p.y * e.b > e.c; + if (e.b < 0) { + above = !above; + } + if (!above) { + fast = 1; + } + } + if (!fast) { + var dxs = topsite.x - e.region.l.x; + above = e.b * (dxp * dxp - dyp * dyp) < dxs * dyp * (1 + 2 * dxp / dxs + e.b * e.b); + if (e.b < 0) { + above = !above; + } + } + } else { + var yl = e.c - e.a * p.x, t1 = p.y - yl, t2 = p.x - topsite.x, t3 = yl - topsite.y; + above = t1 * t1 > t2 * t2 + t3 * t3; + } + return he.side === "l" ? above : !above; + }, + endPoint: function(edge, side, site) { + edge.ep[side] = site; + if (!edge.ep[d3_voronoi_opposite[side]]) return; + callback(edge); + }, + distance: function(s, t) { + var dx = s.x - t.x, dy = s.y - t.y; + return Math.sqrt(dx * dx + dy * dy); + } + }; + var EventQueue = { + list: [], + insert: function(he, site, offset) { + he.vertex = site; + he.ystar = site.y + offset; + for (var i = 0, list = EventQueue.list, l = list.length; i < l; i++) { + var next = list[i]; + if (he.ystar > next.ystar || he.ystar == next.ystar && site.x > next.vertex.x) { + continue; + } else { + break; + } + } + list.splice(i, 0, he); + }, + del: function(he) { + for (var i = 0, ls = EventQueue.list, l = ls.length; i < l && ls[i] != he; ++i) {} + ls.splice(i, 1); + }, + empty: function() { + return EventQueue.list.length === 0; + }, + nextEvent: function(he) { + for (var i = 0, ls = EventQueue.list, l = ls.length; i < l; ++i) { + if (ls[i] == he) return ls[i + 1]; + } + return null; + }, + min: function() { + var elem = EventQueue.list[0]; + return { + x: elem.vertex.x, + y: elem.ystar + }; + }, + extractMin: function() { + return EventQueue.list.shift(); + } + }; + EdgeList.init(); + Sites.bottomSite = Sites.list.shift(); + var newSite = Sites.list.shift(), newIntStar; + var lbnd, rbnd, llbnd, rrbnd, bisector; + var bot, top, temp, p, v; + var e, pm; + while (true) { + if (!EventQueue.empty()) { + newIntStar = EventQueue.min(); + } + if (newSite && (EventQueue.empty() || newSite.y < newIntStar.y || newSite.y == newIntStar.y && newSite.x < newIntStar.x)) { + lbnd = EdgeList.leftBound(newSite); + rbnd = EdgeList.right(lbnd); + bot = EdgeList.rightRegion(lbnd); + e = Geom.bisect(bot, newSite); + bisector = EdgeList.createHalfEdge(e, "l"); + EdgeList.insert(lbnd, bisector); + p = Geom.intersect(lbnd, bisector); + if (p) { + EventQueue.del(lbnd); + EventQueue.insert(lbnd, p, Geom.distance(p, newSite)); + } + lbnd = bisector; + bisector = EdgeList.createHalfEdge(e, "r"); + EdgeList.insert(lbnd, bisector); + p = Geom.intersect(bisector, rbnd); + if (p) { + EventQueue.insert(bisector, p, Geom.distance(p, newSite)); + } + newSite = Sites.list.shift(); + } else if (!EventQueue.empty()) { + lbnd = EventQueue.extractMin(); + llbnd = EdgeList.left(lbnd); + rbnd = EdgeList.right(lbnd); + rrbnd = EdgeList.right(rbnd); + bot = EdgeList.leftRegion(lbnd); + top = EdgeList.rightRegion(rbnd); + v = lbnd.vertex; + Geom.endPoint(lbnd.edge, lbnd.side, v); + Geom.endPoint(rbnd.edge, rbnd.side, v); + EdgeList.del(lbnd); + EventQueue.del(rbnd); + EdgeList.del(rbnd); + pm = "l"; + if (bot.y > top.y) { + temp = bot; + bot = top; + top = temp; + pm = "r"; + } + e = Geom.bisect(bot, top); + bisector = EdgeList.createHalfEdge(e, pm); + EdgeList.insert(llbnd, bisector); + Geom.endPoint(e, d3_voronoi_opposite[pm], v); + p = Geom.intersect(llbnd, bisector); + if (p) { + EventQueue.del(llbnd); + EventQueue.insert(llbnd, p, Geom.distance(p, bot)); + } + p = Geom.intersect(bisector, rrbnd); + if (p) { + EventQueue.insert(bisector, p, Geom.distance(p, bot)); + } + } else { + break; + } + } + for (lbnd = EdgeList.right(EdgeList.leftEnd); lbnd != EdgeList.rightEnd; lbnd = EdgeList.right(lbnd)) { + callback(lbnd.edge); + } + } + d3.geom.delaunay = function(vertices) { + var edges = vertices.map(function() { + return []; + }), triangles = []; + d3_voronoi_tessellate(vertices, function(e) { + edges[e.region.l.index].push(vertices[e.region.r.index]); + }); + edges.forEach(function(edge, i) { + var v = vertices[i], cx = v[0], cy = v[1]; + edge.forEach(function(v) { + v.angle = Math.atan2(v[0] - cx, v[1] - cy); + }); + edge.sort(function(a, b) { + return a.angle - b.angle; + }); + for (var j = 0, m = edge.length - 1; j < m; j++) { + triangles.push([ v, edge[j], edge[j + 1] ]); + } + }); + return triangles; + }; + d3.geom.quadtree = function(points, x1, y1, x2, y2) { + var p, i = -1, n = points.length; + if (arguments.length < 5) { + if (arguments.length === 3) { + y2 = y1; + x2 = x1; + y1 = x1 = 0; + } else { + x1 = y1 = Infinity; + x2 = y2 = -Infinity; + while (++i < n) { + p = points[i]; + if (p.x < x1) x1 = p.x; + if (p.y < y1) y1 = p.y; + if (p.x > x2) x2 = p.x; + if (p.y > y2) y2 = p.y; + } + } + } + var dx = x2 - x1, dy = y2 - y1; + if (dx > dy) y2 = y1 + dx; else x2 = x1 + dy; + function insert(n, p, x1, y1, x2, y2) { + if (isNaN(p.x) || isNaN(p.y)) return; + if (n.leaf) { + var v = n.point; + if (v) { + if (Math.abs(v.x - p.x) + Math.abs(v.y - p.y) < .01) { + insertChild(n, p, x1, y1, x2, y2); + } else { + n.point = null; + insertChild(n, v, x1, y1, x2, y2); + insertChild(n, p, x1, y1, x2, y2); + } + } else { + n.point = p; + } + } else { + insertChild(n, p, x1, y1, x2, y2); + } + } + function insertChild(n, p, x1, y1, x2, y2) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = p.x >= sx, bottom = p.y >= sy, i = (bottom << 1) + right; + n.leaf = false; + n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); + if (right) x1 = sx; else x2 = sx; + if (bottom) y1 = sy; else y2 = sy; + insert(n, p, x1, y1, x2, y2); + } + var root = d3_geom_quadtreeNode(); + root.add = function(p) { + insert(root, p, x1, y1, x2, y2); + }; + root.visit = function(f) { + d3_geom_quadtreeVisit(f, root, x1, y1, x2, y2); + }; + points.forEach(root.add); + return root; + }; + function d3_geom_quadtreeNode() { + return { + leaf: true, + nodes: [], + point: null + }; + } + function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { + if (!f(node, x1, y1, x2, y2)) { + var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; + if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); + if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); + if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); + if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); + } + } + d3.time = {}; + var d3_time = Date, d3_time_daySymbols = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ]; + function d3_time_utc() { + this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); + } + d3_time_utc.prototype = { + getDate: function() { + return this._.getUTCDate(); + }, + getDay: function() { + return this._.getUTCDay(); + }, + getFullYear: function() { + return this._.getUTCFullYear(); + }, + getHours: function() { + return this._.getUTCHours(); + }, + getMilliseconds: function() { + return this._.getUTCMilliseconds(); + }, + getMinutes: function() { + return this._.getUTCMinutes(); + }, + getMonth: function() { + return this._.getUTCMonth(); + }, + getSeconds: function() { + return this._.getUTCSeconds(); + }, + getTime: function() { + return this._.getTime(); + }, + getTimezoneOffset: function() { + return 0; + }, + valueOf: function() { + return this._.valueOf(); + }, + setDate: function() { + d3_time_prototype.setUTCDate.apply(this._, arguments); + }, + setDay: function() { + d3_time_prototype.setUTCDay.apply(this._, arguments); + }, + setFullYear: function() { + d3_time_prototype.setUTCFullYear.apply(this._, arguments); + }, + setHours: function() { + d3_time_prototype.setUTCHours.apply(this._, arguments); + }, + setMilliseconds: function() { + d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); + }, + setMinutes: function() { + d3_time_prototype.setUTCMinutes.apply(this._, arguments); + }, + setMonth: function() { + d3_time_prototype.setUTCMonth.apply(this._, arguments); + }, + setSeconds: function() { + d3_time_prototype.setUTCSeconds.apply(this._, arguments); + }, + setTime: function() { + d3_time_prototype.setTime.apply(this._, arguments); + } + }; + var d3_time_prototype = Date.prototype; + var d3_time_formatDateTime = "%a %b %e %X %Y", d3_time_formatDate = "%m/%d/%Y", d3_time_formatTime = "%H:%M:%S"; + var d3_time_days = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], d3_time_dayAbbreviations = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], d3_time_months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], d3_time_monthAbbreviations = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]; + d3.time.format = function(template) { + var n = template.length; + function format(date) { + var string = [], i = -1, j = 0, c, p, f; + while (++i < n) { + if (template.charCodeAt(i) === 37) { + string.push(template.substring(j, i)); + if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); + if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); + string.push(c); + j = i + 1; + } + } + string.push(template.substring(j, i)); + return string.join(""); + } + format.parse = function(string) { + var d = { + y: 1900, + m: 0, + d: 1, + H: 0, + M: 0, + S: 0, + L: 0 + }, i = d3_time_parse(d, template, string, 0); + if (i != string.length) return null; + if ("p" in d) d.H = d.H % 12 + d.p * 12; + var date = new d3_time(); + date.setFullYear(d.y, d.m, d.d); + date.setHours(d.H, d.M, d.S, d.L); + return date; + }; + format.toString = function() { + return template; + }; + return format; + }; + function d3_time_parse(date, template, string, j) { + var c, p, i = 0, n = template.length, m = string.length; + while (i < n) { + if (j >= m) return -1; + c = template.charCodeAt(i++); + if (c === 37) { + p = d3_time_parsers[template.charAt(i++)]; + if (!p || (j = p(date, string, j)) < 0) return -1; + } else if (c != string.charCodeAt(j++)) { + return -1; + } + } + return j; + } + function d3_time_formatRe(names) { + return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); + } + function d3_time_formatLookup(names) { + var map = new d3_Map(), i = -1, n = names.length; + while (++i < n) map.set(names[i].toLowerCase(), i); + return map; + } + function d3_time_formatPad(value, fill, width) { + value += ""; + var length = value.length; + return length < width ? new Array(width - length + 1).join(fill) + value : value; + } + var d3_time_dayRe = d3_time_formatRe(d3_time_days), d3_time_dayAbbrevRe = d3_time_formatRe(d3_time_dayAbbreviations), d3_time_monthRe = d3_time_formatRe(d3_time_months), d3_time_monthLookup = d3_time_formatLookup(d3_time_months), d3_time_monthAbbrevRe = d3_time_formatRe(d3_time_monthAbbreviations), d3_time_monthAbbrevLookup = d3_time_formatLookup(d3_time_monthAbbreviations); + var d3_time_formatPads = { + "-": "", + _: " ", + "0": "0" + }; + var d3_time_formats = { + a: function(d) { + return d3_time_dayAbbreviations[d.getDay()]; + }, + A: function(d) { + return d3_time_days[d.getDay()]; + }, + b: function(d) { + return d3_time_monthAbbreviations[d.getMonth()]; + }, + B: function(d) { + return d3_time_months[d.getMonth()]; + }, + c: d3.time.format(d3_time_formatDateTime), + d: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + e: function(d, p) { + return d3_time_formatPad(d.getDate(), p, 2); + }, + H: function(d, p) { + return d3_time_formatPad(d.getHours(), p, 2); + }, + I: function(d, p) { + return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); + }, + j: function(d, p) { + return d3_time_formatPad(1 + d3.time.dayOfYear(d), p, 3); + }, + L: function(d, p) { + return d3_time_formatPad(d.getMilliseconds(), p, 3); + }, + m: function(d, p) { + return d3_time_formatPad(d.getMonth() + 1, p, 2); + }, + M: function(d, p) { + return d3_time_formatPad(d.getMinutes(), p, 2); + }, + p: function(d) { + return d.getHours() >= 12 ? "PM" : "AM"; + }, + S: function(d, p) { + return d3_time_formatPad(d.getSeconds(), p, 2); + }, + U: function(d, p) { + return d3_time_formatPad(d3.time.sundayOfYear(d), p, 2); + }, + w: function(d) { + return d.getDay(); + }, + W: function(d, p) { + return d3_time_formatPad(d3.time.mondayOfYear(d), p, 2); + }, + x: d3.time.format(d3_time_formatDate), + X: d3.time.format(d3_time_formatTime), + y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 100, p, 2); + }, + Y: function(d, p) { + return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); + }, + Z: d3_time_zone, + "%": function() { + return "%"; + } + }; + var d3_time_parsers = { + a: d3_time_parseWeekdayAbbrev, + A: d3_time_parseWeekday, + b: d3_time_parseMonthAbbrev, + B: d3_time_parseMonth, + c: d3_time_parseLocaleFull, + d: d3_time_parseDay, + e: d3_time_parseDay, + H: d3_time_parseHour24, + I: d3_time_parseHour24, + L: d3_time_parseMilliseconds, + m: d3_time_parseMonthNumber, + M: d3_time_parseMinutes, + p: d3_time_parseAmPm, + S: d3_time_parseSeconds, + x: d3_time_parseLocaleDate, + X: d3_time_parseLocaleTime, + y: d3_time_parseYear, + Y: d3_time_parseFullYear + }; + function d3_time_parseWeekdayAbbrev(date, string, i) { + d3_time_dayAbbrevRe.lastIndex = 0; + var n = d3_time_dayAbbrevRe.exec(string.substring(i)); + return n ? i += n[0].length : -1; + } + function d3_time_parseWeekday(date, string, i) { + d3_time_dayRe.lastIndex = 0; + var n = d3_time_dayRe.exec(string.substring(i)); + return n ? i += n[0].length : -1; + } + function d3_time_parseMonthAbbrev(date, string, i) { + d3_time_monthAbbrevRe.lastIndex = 0; + var n = d3_time_monthAbbrevRe.exec(string.substring(i)); + return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i += n[0].length) : -1; + } + function d3_time_parseMonth(date, string, i) { + d3_time_monthRe.lastIndex = 0; + var n = d3_time_monthRe.exec(string.substring(i)); + return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i += n[0].length) : -1; + } + function d3_time_parseLocaleFull(date, string, i) { + return d3_time_parse(date, d3_time_formats.c.toString(), string, i); + } + function d3_time_parseLocaleDate(date, string, i) { + return d3_time_parse(date, d3_time_formats.x.toString(), string, i); + } + function d3_time_parseLocaleTime(date, string, i) { + return d3_time_parse(date, d3_time_formats.X.toString(), string, i); + } + function d3_time_parseFullYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 4)); + return n ? (date.y = +n[0], i += n[0].length) : -1; + } + function d3_time_parseYear(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.y = d3_time_expandYear(+n[0]), i += n[0].length) : -1; + } + function d3_time_expandYear(d) { + return d + (d > 68 ? 1900 : 2e3); + } + function d3_time_parseMonthNumber(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.m = n[0] - 1, i += n[0].length) : -1; + } + function d3_time_parseDay(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.d = +n[0], i += n[0].length) : -1; + } + function d3_time_parseHour24(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.H = +n[0], i += n[0].length) : -1; + } + function d3_time_parseMinutes(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.M = +n[0], i += n[0].length) : -1; + } + function d3_time_parseSeconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 2)); + return n ? (date.S = +n[0], i += n[0].length) : -1; + } + function d3_time_parseMilliseconds(date, string, i) { + d3_time_numberRe.lastIndex = 0; + var n = d3_time_numberRe.exec(string.substring(i, i + 3)); + return n ? (date.L = +n[0], i += n[0].length) : -1; + } + var d3_time_numberRe = /^\s*\d+/; + function d3_time_parseAmPm(date, string, i) { + var n = d3_time_amPmLookup.get(string.substring(i, i += 2).toLowerCase()); + return n == null ? -1 : (date.p = n, i); + } + var d3_time_amPmLookup = d3.map({ + am: 0, + pm: 1 + }); + function d3_time_zone(d) { + var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(Math.abs(z) / 60), zm = Math.abs(z) % 60; + return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); + } + d3.time.format.utc = function(template) { + var local = d3.time.format(template); + function format(date) { + try { + d3_time = d3_time_utc; + var utc = new d3_time(); + utc._ = date; + return local(utc); + } finally { + d3_time = Date; + } + } + format.parse = function(string) { + try { + d3_time = d3_time_utc; + var date = local.parse(string); + return date && date._; + } finally { + d3_time = Date; + } + }; + format.toString = local.toString; + return format; + }; + var d3_time_formatIso = d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ"); + d3.time.format.iso = Date.prototype.toISOString ? d3_time_formatIsoNative : d3_time_formatIso; + function d3_time_formatIsoNative(date) { + return date.toISOString(); + } + d3_time_formatIsoNative.parse = function(string) { + var date = new Date(string); + return isNaN(date) ? null : date; + }; + d3_time_formatIsoNative.toString = d3_time_formatIso.toString; + function d3_time_interval(local, step, number) { + function round(date) { + var d0 = local(date), d1 = offset(d0, 1); + return date - d0 < d1 - date ? d0 : d1; + } + function ceil(date) { + step(date = local(new d3_time(date - 1)), 1); + return date; + } + function offset(date, k) { + step(date = new d3_time(+date), k); + return date; + } + function range(t0, t1, dt) { + var time = ceil(t0), times = []; + if (dt > 1) { + while (time < t1) { + if (!(number(time) % dt)) times.push(new Date(+time)); + step(time, 1); + } + } else { + while (time < t1) times.push(new Date(+time)), step(time, 1); + } + return times; + } + function range_utc(t0, t1, dt) { + try { + d3_time = d3_time_utc; + var utc = new d3_time_utc(); + utc._ = t0; + return range(utc, t1, dt); + } finally { + d3_time = Date; + } + } + local.floor = local; + local.round = round; + local.ceil = ceil; + local.offset = offset; + local.range = range; + var utc = local.utc = d3_time_interval_utc(local); + utc.floor = utc; + utc.round = d3_time_interval_utc(round); + utc.ceil = d3_time_interval_utc(ceil); + utc.offset = d3_time_interval_utc(offset); + utc.range = range_utc; + return local; + } + function d3_time_interval_utc(method) { + return function(date, k) { + try { + d3_time = d3_time_utc; + var utc = new d3_time_utc(); + utc._ = date; + return method(utc, k)._; + } finally { + d3_time = Date; + } + }; + } + d3.time.second = d3_time_interval(function(date) { + return new d3_time(Math.floor(date / 1e3) * 1e3); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 1e3); + }, function(date) { + return date.getSeconds(); + }); + d3.time.seconds = d3.time.second.range; + d3.time.seconds.utc = d3.time.second.utc.range; + d3.time.minute = d3_time_interval(function(date) { + return new d3_time(Math.floor(date / 6e4) * 6e4); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 6e4); + }, function(date) { + return date.getMinutes(); + }); + d3.time.minutes = d3.time.minute.range; + d3.time.minutes.utc = d3.time.minute.utc.range; + d3.time.hour = d3_time_interval(function(date) { + var timezone = date.getTimezoneOffset() / 60; + return new d3_time((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); + }, function(date, offset) { + date.setTime(date.getTime() + Math.floor(offset) * 36e5); + }, function(date) { + return date.getHours(); + }); + d3.time.hours = d3.time.hour.range; + d3.time.hours.utc = d3.time.hour.utc.range; + d3.time.day = d3_time_interval(function(date) { + var day = new d3_time(1970, 0); + day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); + return day; + }, function(date, offset) { + date.setDate(date.getDate() + offset); + }, function(date) { + return date.getDate() - 1; + }); + d3.time.days = d3.time.day.range; + d3.time.days.utc = d3.time.day.utc.range; + d3.time.dayOfYear = function(date) { + var year = d3.time.year(date); + return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); + }; + d3_time_daySymbols.forEach(function(day, i) { + day = day.toLowerCase(); + i = 7 - i; + var interval = d3.time[day] = d3_time_interval(function(date) { + (date = d3.time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); + return date; + }, function(date, offset) { + date.setDate(date.getDate() + Math.floor(offset) * 7); + }, function(date) { + var day = d3.time.year(date).getDay(); + return Math.floor((d3.time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); + }); + d3.time[day + "s"] = interval.range; + d3.time[day + "s"].utc = interval.utc.range; + d3.time[day + "OfYear"] = function(date) { + var day = d3.time.year(date).getDay(); + return Math.floor((d3.time.dayOfYear(date) + (day + i) % 7) / 7); + }; + }); + d3.time.week = d3.time.sunday; + d3.time.weeks = d3.time.sunday.range; + d3.time.weeks.utc = d3.time.sunday.utc.range; + d3.time.weekOfYear = d3.time.sundayOfYear; + d3.time.month = d3_time_interval(function(date) { + date = d3.time.day(date); + date.setDate(1); + return date; + }, function(date, offset) { + date.setMonth(date.getMonth() + offset); + }, function(date) { + return date.getMonth(); + }); + d3.time.months = d3.time.month.range; + d3.time.months.utc = d3.time.month.utc.range; + d3.time.year = d3_time_interval(function(date) { + date = d3.time.day(date); + date.setMonth(0, 1); + return date; + }, function(date, offset) { + date.setFullYear(date.getFullYear() + offset); + }, function(date) { + return date.getFullYear(); + }); + d3.time.years = d3.time.year.range; + d3.time.years.utc = d3.time.year.utc.range; + function d3_time_scale(linear, methods, format) { + function scale(x) { + return linear(x); + } + scale.invert = function(x) { + return d3_time_scaleDate(linear.invert(x)); + }; + scale.domain = function(x) { + if (!arguments.length) return linear.domain().map(d3_time_scaleDate); + linear.domain(x); + return scale; + }; + scale.nice = function(m) { + return scale.domain(d3_scale_nice(scale.domain(), function() { + return m; + })); + }; + scale.ticks = function(m, k) { + var extent = d3_time_scaleExtent(scale.domain()); + if (typeof m !== "function") { + var span = extent[1] - extent[0], target = span / m, i = d3.bisect(d3_time_scaleSteps, target); + if (i == d3_time_scaleSteps.length) return methods.year(extent, m); + if (!i) return linear.ticks(m).map(d3_time_scaleDate); + if (Math.log(target / d3_time_scaleSteps[i - 1]) < Math.log(d3_time_scaleSteps[i] / target)) --i; + m = methods[i]; + k = m[1]; + m = m[0].range; + } + return m(extent[0], new Date(+extent[1] + 1), k); + }; + scale.tickFormat = function() { + return format; + }; + scale.copy = function() { + return d3_time_scale(linear.copy(), methods, format); + }; + return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); + } + function d3_time_scaleExtent(domain) { + var start = domain[0], stop = domain[domain.length - 1]; + return start < stop ? [ start, stop ] : [ stop, start ]; + } + function d3_time_scaleDate(t) { + return new Date(t); + } + function d3_time_scaleFormat(formats) { + return function(date) { + var i = formats.length - 1, f = formats[i]; + while (!f[1](date)) f = formats[--i]; + return f[0](date); + }; + } + function d3_time_scaleSetYear(y) { + var d = new Date(y, 0, 1); + d.setFullYear(y); + return d; + } + function d3_time_scaleGetYear(d) { + var y = d.getFullYear(), d0 = d3_time_scaleSetYear(y), d1 = d3_time_scaleSetYear(y + 1); + return y + (d - d0) / (d1 - d0); + } + var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; + var d3_time_scaleLocalMethods = [ [ d3.time.second, 1 ], [ d3.time.second, 5 ], [ d3.time.second, 15 ], [ d3.time.second, 30 ], [ d3.time.minute, 1 ], [ d3.time.minute, 5 ], [ d3.time.minute, 15 ], [ d3.time.minute, 30 ], [ d3.time.hour, 1 ], [ d3.time.hour, 3 ], [ d3.time.hour, 6 ], [ d3.time.hour, 12 ], [ d3.time.day, 1 ], [ d3.time.day, 2 ], [ d3.time.week, 1 ], [ d3.time.month, 1 ], [ d3.time.month, 3 ], [ d3.time.year, 1 ] ]; + var d3_time_scaleLocalFormats = [ [ d3.time.format("%Y"), d3_true ], [ d3.time.format("%B"), function(d) { + return d.getMonth(); + } ], [ d3.time.format("%b %d"), function(d) { + return d.getDate() != 1; + } ], [ d3.time.format("%a %d"), function(d) { + return d.getDay() && d.getDate() != 1; + } ], [ d3.time.format("%I %p"), function(d) { + return d.getHours(); + } ], [ d3.time.format("%I:%M"), function(d) { + return d.getMinutes(); + } ], [ d3.time.format(":%S"), function(d) { + return d.getSeconds(); + } ], [ d3.time.format(".%L"), function(d) { + return d.getMilliseconds(); + } ] ]; + var d3_time_scaleLinear = d3.scale.linear(), d3_time_scaleLocalFormat = d3_time_scaleFormat(d3_time_scaleLocalFormats); + d3_time_scaleLocalMethods.year = function(extent, m) { + return d3_time_scaleLinear.domain(extent.map(d3_time_scaleGetYear)).ticks(m).map(d3_time_scaleSetYear); + }; + d3.time.scale = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); + }; + var d3_time_scaleUTCMethods = d3_time_scaleLocalMethods.map(function(m) { + return [ m[0].utc, m[1] ]; + }); + var d3_time_scaleUTCFormats = [ [ d3.time.format.utc("%Y"), d3_true ], [ d3.time.format.utc("%B"), function(d) { + return d.getUTCMonth(); + } ], [ d3.time.format.utc("%b %d"), function(d) { + return d.getUTCDate() != 1; + } ], [ d3.time.format.utc("%a %d"), function(d) { + return d.getUTCDay() && d.getUTCDate() != 1; + } ], [ d3.time.format.utc("%I %p"), function(d) { + return d.getUTCHours(); + } ], [ d3.time.format.utc("%I:%M"), function(d) { + return d.getUTCMinutes(); + } ], [ d3.time.format.utc(":%S"), function(d) { + return d.getUTCSeconds(); + } ], [ d3.time.format.utc(".%L"), function(d) { + return d.getUTCMilliseconds(); + } ] ]; + var d3_time_scaleUTCFormat = d3_time_scaleFormat(d3_time_scaleUTCFormats); + function d3_time_scaleUTCSetYear(y) { + var d = new Date(Date.UTC(y, 0, 1)); + d.setUTCFullYear(y); + return d; + } + function d3_time_scaleUTCGetYear(d) { + var y = d.getUTCFullYear(), d0 = d3_time_scaleUTCSetYear(y), d1 = d3_time_scaleUTCSetYear(y + 1); + return y + (d - d0) / (d1 - d0); + } + d3_time_scaleUTCMethods.year = function(extent, m) { + return d3_time_scaleLinear.domain(extent.map(d3_time_scaleUTCGetYear)).ticks(m).map(d3_time_scaleUTCSetYear); + }; + d3.time.scale.utc = function() { + return d3_time_scale(d3.scale.linear(), d3_time_scaleUTCMethods, d3_time_scaleUTCFormat); + }; + return d3; +}(); \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/description.js b/sproutcore/apps/fp/resources/description.js new file mode 100644 index 000000000..9154df2ae --- /dev/null +++ b/sproutcore/apps/fp/resources/description.js @@ -0,0 +1,9 @@ + + +function createDescription() { + var description = "Neque porro quisquam est, qui doLorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam hendrerit sagittis eros sagittis suscipit. In orci dolor, rutrum sed faucibus nec, rhoncus id nibh. Vivamus tristique mattis venenatis. Curabitur in eros odio. Aenean vel nibh iaculis, faucibus justo at, suscipit turpis. Sed lacinia urna non risus tempus blandit."; + + + d3.select('.descriptionBlock') + .text(description); +} diff --git a/sproutcore/apps/fp/resources/donutChartMaker.js b/sproutcore/apps/fp/resources/donutChartMaker.js new file mode 100644 index 000000000..368eb3ce0 --- /dev/null +++ b/sproutcore/apps/fp/resources/donutChartMaker.js @@ -0,0 +1,305 @@ +d3.edge = {}; + +d3.edge.donutChart = function module() { + + var colors = { + "emplAndOffice" : "#72587F", + "mixedUse" : "#BE1E2D", + "res" : "#FFA824", + "civic" : "#1C75BC", + "parks" : "#009444", + "streets" : "#E0E0E0", + "multiFam" : "#C88B3F", + "townhome" : "#EDBF77", + "sfSmallLot" : "#FDEE2C", + "sfLargeLot" : "#FEFAE1", + "retail" : "#B73771", + "industrial" : "#EFEAF3" + } + + // this function is used to determine whether text should be black or white, depending on whether the color of the + // element it is overlayed upon 'isDark' + + function isDark(color) { + var rVal = color.substring(1,3); + var gVal = color.substring(3,5); + var bVal = color.substring(5,7); + + var grayscale = (parseInt(rVal,16) + parseInt(gVal,16) + parseInt(bVal,16))/3; + return (grayscale < 150) ? true : false; + } + + /*** + * @param _selection This is a div (or multiple divs) that will hold the donutChart; this div must be pre-bound with the data + * for the chart. Data should be an array of objects, each of which will make up one piece of the "donut" + * + * These objects should be of the form: + * + { + "category" : "Parks", + "percentage" : 0.90 + } + + the function d3.edge.dataManager is helpful for creating these objects + */ + + function exports(_selection) { + _selection.each(function(_data){ + + + var chartWidth = Math.min($(this).height(), $(this).width()); + chartHeight = chartWidth, + radius = chartHeight / 2, + outerRadiusPct = .85, + innerRadiusPct = .65; + + var arcColors = d3.scale.ordinal() + .domain( + ["Mixed-Use", "Residential", "Employment", "Civic", "Parks", "Streets", + "Multifamily", "Townhome", "Single Family Small Lot", "Single Family Large Lot", + "Retail", "Office", "Industrial"]) + .range([colors.mixedUse, colors.res, colors.emplAndOffice, colors.civic, colors.parks, colors.streets, + colors.multiFam, colors.townhome, colors.sfSmallLot, colors.sfLargeLot, + colors.retail, colors.emplAndOffice, colors.industrial]); + + var arc = d3.svg.arc() + .outerRadius(radius*outerRadiusPct) + .innerRadius(radius*innerRadiusPct); + + // Used to transition the donut chart from one set of data to the next + function arcTween(a) { + var i = d3.interpolate(this._current, a); + this._current = i(0); + return function(t) { + return arc(i(t)); + }; + } + var pie = d3.layout.pie() + .value(function(d) { return d.percentage; }); + + // If any of the percentages are 0 (or invalid) they should not be used in the construction of the chart, as + // this will cause errors with the d3 arc + var nonEmptyData = _data.filter(function(d) { + if (!isNaN(d.percentage) && d.percentage != 0) { + return d; + } + }); + + var svg = d3.select(this) + .selectAll("svg") + .data([nonEmptyData]); + + // By using the "container" for newly entered elements, we don't have to worry about appending new elements to + // the dom each time we refresh; container is only created on the first time through + var container = svg.enter().append("svg"); + + svg.attr("width",chartWidth) + .attr("height",chartHeight) + .classed("chart", true); + + container.append("g").attr("class", "chart_and_legend"); +// svg.selectAll('.chart_and_legend') +// .data([_data]).enter().append('g'); + + var arcs = svg.select(".chart_and_legend") + .selectAll(".arc") + .data(pie(nonEmptyData)); + + arcs.enter().append("g"); + arcs.attr("class", "arc"); + + var chart_and_legend = svg.select(".chart_and_legend"); + chart_and_legend.attr("transform", "translate(" + $(this).width() / 2 + "," + ($(this).height() / 2) + ")"); + + + arcs.exit().remove(); + + arcs.each(function(d, i) { + var arcPaths = d3.select(this).selectAll("path") + .data([d]); + arcPaths.transition().duration(750).attrTween("d", arcTween); + + arcPaths.enter().append('path') + .attr("d", arc) + .each(function(d) { this._current = d; }); + + arcPaths.style("fill", arcColors(d.data.category)); + + // If the percentage is less than two, the sliver of the pie is really too small to label + // You'll still be able to see the value in the tooltip + if (d.data.percentage > 0.02 ) { + + var arcText = d3.select(this).selectAll("text") + .data([d]); + + arcText.enter().append('text'); + arcText.exit().remove(); + arcText.attr("transform", function() { + var translation = arc.centroid(d); + var rotation = (d.endAngle + d.startAngle)/2 * (180/Math.PI); + if ((90 < rotation) && (rotation < 270)) { + rotation += 180; + } + return "translate(" + translation + ")" + "rotate(" + rotation + ")"; + }) + .attr("dy", ".35em") + .style("text-anchor", "middle") + .text(function() { + perct= d.data.percentage*100; + if (perct != 0) { + formattedPerct = perct.toFixed(0) + "%"; + }else {formattedPerct = ""} + return formattedPerct; }) + .style("fill", function() { + var arcColor = arcColors(d.data.category); + return (isDark(arcColor)) ? "white" : "black"; + }); + } else { + + var arcText = d3.select(this).selectAll("text") + .remove(); + + } + + var arcTooltip = d3.select(this).selectAll("title") + .data([d]); + + arcTooltip.enter().append('svg:title'); + arcTooltip + .text(function() { + perct= d.data.percentage*100; + if (perct != 0) { + formattedPerct = perct.toFixed(0) + "%"; + }else {formattedPerct = ""} + return d.data.category + ", " + formattedPerct; }); + + }); + + var lineHeight = chartHeight*.06, + keyRectSize = lineHeight*.90, + fontSize = chartHeight*0.05, + keyRectOffset = keyRectSize/5; + + var maxLegendItemLength = 0, + numLegendItems = 0; + + var legend = chart_and_legend.selectAll('.legend') + .data([0]); + legend.enter().append("g"); + legend.attr("class", "legend"); + + var legendItems = legend.selectAll(".legendItems") + .data(nonEmptyData); + + legendItems.enter().append("g"); + legendItems.exit().remove(); + legendItems.attr("class", "legendItems"); + + var doubleLineLabels = 0; + + legendItems.each(function(d, i) { + + + $(this).attr("transform", "translate(0, " + (i*lineHeight + doubleLineLabels*lineHeight) + ")"); + + var label = d.category; + var label_length = d.category.length; + + // If the name of the category is longer than 15 characters and is made up of more than 2 words + // split it into two lines + + var legendText = d3.select(this).selectAll('text') + .data([d]); + legendText.enter().append('text'); + legendText.exit().remove(); + legendText + .attr("y",keyRectSize/2) + .attr("dy", ".35em") + .style("text-anchor","end") + .style("font-size", fontSize) + .attr("transform, translate(0," + i*lineHeight + ")"); + + + if (label_length > 15 && label.split(" ").length > 2) { + var label_first_half, label_second_half; + + label_first_half = label.split(" ").slice(0,2).join(" "); + label_second_half = label.split(" ").slice(2).join(" "); + + legendText.text(label_first_half); + + if(label_first_half.length > maxLegendItemLength) { + maxLegendItemLength = label_first_half.length; + } + + var legendTextLine2 = d3.select(this).selectAll('.line2') + .data([d]); + legendTextLine2.enter().append('text'); + legendTextLine2 + .attr("y", keyRectSize*3/2) + .attr("dy", ".35em") + .style("text-anchor","end") + .style("font-size", fontSize) + .text(label_second_half); + + numLegendItems +=2; + doubleLineLabels += 1; + + + } else { + + legendText.text(label); + + if(label_length > maxLegendItemLength) { + maxLegendItemLength = label_length; + } + numLegendItems++; + } + + var legendKeyRect = d3.select(this).selectAll('rect') + .data([d]); + legendKeyRect.enter().append('rect'); + legendKeyRect + .attr("width", keyRectSize) + .attr("height", keyRectSize) + .attr("x", keyRectOffset) + .style("fill",function (d) { + return arcColors(d.category); + }); + + var legendItemTooltip = d3.select(this).selectAll("title") + .data([d]); + + legendItemTooltip.enter().append('svg:title'); + legendItemTooltip + .text(function() { + perct= d.percentage*100; + if (perct != 0) { + formattedPerct = perct.toFixed(0) + "%"; + }else {formattedPerct = ""} + return d.category + ", " + formattedPerct; }); + }); + + + var legendHeight = numLegendItems*keyRectSize, + legendWidth = maxLegendItemLength*5 + keyRectSize; + + legend.attr("transform", "translate(" + (legendWidth - 2*(keyRectSize + keyRectOffset)) / 2 + "," + (-legendHeight / 2) + ")"); + + }); + } + return exports; + +}; +d3.edge.dataManager = function module() { + + var exports = {}; + exports.createDataObject = function(_category, _percentage) { + var dataObject = { + "category" : _category, + "percentage" : parseFloat(_percentage) + } + return dataObject; + } + return exports; +}; diff --git a/sproutcore/apps/fp/resources/images/FakeMap.png b/sproutcore/apps/fp/resources/images/FakeMap.png new file mode 100644 index 000000000..e69de29bb diff --git a/sproutcore/apps/fp/resources/images/add.png b/sproutcore/apps/fp/resources/images/add.png new file mode 100755 index 000000000..93a9277a2 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/add.png differ diff --git a/sproutcore/apps/fp/resources/images/clear.png b/sproutcore/apps/fp/resources/images/clear.png new file mode 100755 index 000000000..66c8d8805 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/clear.png differ diff --git a/sproutcore/apps/fp/resources/images/colorpicker/bar-alpha.png b/sproutcore/apps/fp/resources/images/colorpicker/bar-alpha.png new file mode 100644 index 000000000..2950daeb8 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/colorpicker/bar-alpha.png differ diff --git a/sproutcore/apps/fp/resources/images/colorpicker/bar-opacity.png b/sproutcore/apps/fp/resources/images/colorpicker/bar-opacity.png new file mode 100644 index 000000000..e42ad0812 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/colorpicker/bar-opacity.png differ diff --git a/sproutcore/apps/fp/resources/images/colorpicker/bar-pointer.png b/sproutcore/apps/fp/resources/images/colorpicker/bar-pointer.png new file mode 100644 index 000000000..6e980cfa7 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/colorpicker/bar-pointer.png differ diff --git a/sproutcore/apps/fp/resources/images/colorpicker/bar.png b/sproutcore/apps/fp/resources/images/colorpicker/bar.png new file mode 100644 index 000000000..80eb2bbe7 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/colorpicker/bar.png differ diff --git a/sproutcore/apps/fp/resources/images/colorpicker/map-opacity.png b/sproutcore/apps/fp/resources/images/colorpicker/map-opacity.png new file mode 100644 index 000000000..6756cee6d Binary files /dev/null and b/sproutcore/apps/fp/resources/images/colorpicker/map-opacity.png differ diff --git a/sproutcore/apps/fp/resources/images/colorpicker/map-pointer.png b/sproutcore/apps/fp/resources/images/colorpicker/map-pointer.png new file mode 100644 index 000000000..64992968b Binary files /dev/null and b/sproutcore/apps/fp/resources/images/colorpicker/map-pointer.png differ diff --git a/sproutcore/apps/fp/resources/images/colorpicker/map.png b/sproutcore/apps/fp/resources/images/colorpicker/map.png new file mode 100644 index 000000000..853d38c68 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/colorpicker/map.png differ diff --git a/sproutcore/apps/fp/resources/images/colorpicker/preview-opacity.png b/sproutcore/apps/fp/resources/images/colorpicker/preview-opacity.png new file mode 100644 index 000000000..0dd9a2f8b Binary files /dev/null and b/sproutcore/apps/fp/resources/images/colorpicker/preview-opacity.png differ diff --git a/sproutcore/apps/fp/resources/images/colorpicker/ui-colorpicker.png b/sproutcore/apps/fp/resources/images/colorpicker/ui-colorpicker.png new file mode 100644 index 000000000..e244c689e Binary files /dev/null and b/sproutcore/apps/fp/resources/images/colorpicker/ui-colorpicker.png differ diff --git a/sproutcore/apps/fp/resources/images/default_logos/uf_thumbnail_24.png b/sproutcore/apps/fp/resources/images/default_logos/uf_thumbnail_24.png new file mode 100644 index 000000000..724b5d835 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/default_logos/uf_thumbnail_24.png differ diff --git a/sproutcore/apps/fp/resources/images/default_logos/uf_thumbnail_35.png b/sproutcore/apps/fp/resources/images/default_logos/uf_thumbnail_35.png new file mode 100644 index 000000000..4c5007fbe Binary files /dev/null and b/sproutcore/apps/fp/resources/images/default_logos/uf_thumbnail_35.png differ diff --git a/sproutcore/apps/fp/resources/images/delete.png b/sproutcore/apps/fp/resources/images/delete.png new file mode 100755 index 000000000..cf68091f9 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/delete.png differ diff --git a/sproutcore/apps/fp/resources/images/error_progress_view_content.png b/sproutcore/apps/fp/resources/images/error_progress_view_content.png new file mode 100644 index 000000000..7dfa4a7a5 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/error_progress_view_content.png differ diff --git a/sproutcore/apps/fp/resources/images/favicon.ico b/sproutcore/apps/fp/resources/images/favicon.ico new file mode 100644 index 000000000..724b5d835 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/favicon.ico differ diff --git a/sproutcore/apps/fp/resources/images/loader.gif b/sproutcore/apps/fp/resources/images/loader.gif new file mode 100644 index 000000000..be7058dac Binary files /dev/null and b/sproutcore/apps/fp/resources/images/loader.gif differ diff --git a/sproutcore/apps/fp/resources/images/loading.png b/sproutcore/apps/fp/resources/images/loading.png new file mode 100755 index 000000000..ddd5b2542 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/loading.png differ diff --git a/sproutcore/apps/fp/resources/images/section_toolbars/pulldown.png b/sproutcore/apps/fp/resources/images/section_toolbars/pulldown.png new file mode 100644 index 000000000..d0de60100 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/section_toolbars/pulldown.png differ diff --git a/sproutcore/apps/fp/resources/images/section_toolbars/uf_thumbnail_24.png b/sproutcore/apps/fp/resources/images/section_toolbars/uf_thumbnail_24.png new file mode 100644 index 000000000..724b5d835 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/section_toolbars/uf_thumbnail_24.png differ diff --git a/sproutcore/apps/fp/resources/images/zoom_to_extent.png b/sproutcore/apps/fp/resources/images/zoom_to_extent.png new file mode 100644 index 000000000..d8e4b1e39 Binary files /dev/null and b/sproutcore/apps/fp/resources/images/zoom_to_extent.png differ diff --git a/sproutcore/apps/fp/resources/jquery_ui/accordion.js b/sproutcore/apps/fp/resources/jquery_ui/accordion.js new file mode 100644 index 000000000..13cf8f791 --- /dev/null +++ b/sproutcore/apps/fp/resources/jquery_ui/accordion.js @@ -0,0 +1,613 @@ +/* + * jQuery UI Accordion 1.8.17 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Accordion + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + */ +sc_require('resources/jquery_ui/jquery_ui_core'); +sc_require('resources/jquery_ui/widget'); + +(function( $, undefined ) { +$.widget( "ui.accordion", { + options: { + active: 0, + animated: "slide", + autoHeight: true, + clearStyle: false, + collapsible: false, + event: "click", + fillSpace: false, + header: "> li > :first-child,> :not(li):even", + icons: { + header: "ui-icon-triangle-1-e", + headerSelected: "ui-icon-triangle-1-s" + }, + navigation: false, + navigationFilter: function() { + return this.href.toLowerCase() === location.href.toLowerCase(); + } + }, + + _create: function() { + var self = this, + options = self.options; + + self.running = 0; + + self.element + .addClass( "ui-accordion ui-widget ui-helper-reset" ) + // in lack of child-selectors in CSS + // we need to mark top-LIs in a UL-accordion for some IE-fix + .children( "li" ) + .addClass( "ui-accordion-li-fix" ); + + self.headers = self.element.find( options.header ) + .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" ) + .bind( "mouseenter.accordion", function() { + if ( options.disabled ) { + return; + } + $( this ).addClass( "ui-state-hover" ); + }) + .bind( "mouseleave.accordion", function() { + if ( options.disabled ) { + return; + } + $( this ).removeClass( "ui-state-hover" ); + }) + .bind( "focus.accordion", function() { + if ( options.disabled ) { + return; + } + $( this ).addClass( "ui-state-focus" ); + }) + .bind( "blur.accordion", function() { + if ( options.disabled ) { + return; + } + $( this ).removeClass( "ui-state-focus" ); + }); + + self.headers.next() + .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" ); + + if ( options.navigation ) { + var current = self.element.find( "a" ).filter( options.navigationFilter ).eq( 0 ); + if ( current.length ) { + var header = current.closest( ".ui-accordion-header" ); + if ( header.length ) { + // anchor within header + self.active = header; + } else { + // anchor within content + self.active = current.closest( ".ui-accordion-content" ).prev(); + } + } + } + + self.active = self._findActive( self.active || options.active ) + .addClass( "ui-state-default ui-state-active" ) + .toggleClass( "ui-corner-all" ) + .toggleClass( "ui-corner-top" ); + self.active.next().addClass( "ui-accordion-content-active" ); + + self._createIcons(); + self.resize(); + + // ARIA + self.element.attr( "role", "tablist" ); + + self.headers + .attr( "role", "tab" ) + .bind( "keydown.accordion", function( event ) { + return self._keydown( event ); + }) + .next() + .attr( "role", "tabpanel" ); + + self.headers + .not( self.active || "" ) + .attr({ + "aria-expanded": "false", + "aria-selected": "false", + tabIndex: -1 + }) + .next() + .hide(); + + // make sure at least one header is in the tab order + if ( !self.active.length ) { + self.headers.eq( 0 ).attr( "tabIndex", 0 ); + } else { + self.active + .attr({ + "aria-expanded": "true", + "aria-selected": "true", + tabIndex: 0 + }); + } + + // only need links in tab order for Safari + if ( !$.browser.safari ) { + self.headers.find( "a" ).attr( "tabIndex", -1 ); + } + + if ( options.event ) { + self.headers.bind( options.event.split(" ").join(".accordion ") + ".accordion", function(event) { + self._clickHandler.call( self, event, this ); + event.preventDefault(); + }); + } + }, + + _createIcons: function() { + var options = this.options; + if ( options.icons ) { + $( "" ) + .addClass( "ui-icon " + options.icons.header ) + .prependTo( this.headers ); + this.active.children( ".ui-icon" ) + .toggleClass(options.icons.header) + .toggleClass(options.icons.headerSelected); + this.element.addClass( "ui-accordion-icons" ); + } + }, + + _destroyIcons: function() { + this.headers.children( ".ui-icon" ).remove(); + this.element.removeClass( "ui-accordion-icons" ); + }, + + destroy: function() { + var options = this.options; + + this.element + .removeClass( "ui-accordion ui-widget ui-helper-reset" ) + .removeAttr( "role" ); + + this.headers + .unbind( ".accordion" ) + .removeClass( "ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" ) + .removeAttr( "role" ) + .removeAttr( "aria-expanded" ) + .removeAttr( "aria-selected" ) + .removeAttr( "tabIndex" ); + + this.headers.find( "a" ).removeAttr( "tabIndex" ); + this._destroyIcons(); + var contents = this.headers.next() + .css( "display", "" ) + .removeAttr( "role" ) + .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled" ); + if ( options.autoHeight || options.fillHeight ) { + contents.css( "height", "" ); + } + + return $.Widget.prototype.destroy.call( this ); + }, + + _setOption: function( key, value ) { + $.Widget.prototype._setOption.apply( this, arguments ); + + if ( key == "active" ) { + this.activate( value ); + } + if ( key == "icons" ) { + this._destroyIcons(); + if ( value ) { + this._createIcons(); + } + } + // #5332 - opacity doesn't cascade to positioned elements in IE + // so we need to add the disabled class to the headers and panels + if ( key == "disabled" ) { + this.headers.add(this.headers.next()) + [ value ? "addClass" : "removeClass" ]( + "ui-accordion-disabled ui-state-disabled" ); + } + }, + + _keydown: function( event ) { + if ( this.options.disabled || event.altKey || event.ctrlKey ) { + return; + } + + var keyCode = $.ui.keyCode, + length = this.headers.length, + currentIndex = this.headers.index( event.target ), + toFocus = false; + + switch ( event.keyCode ) { + case keyCode.RIGHT: + case keyCode.DOWN: + toFocus = this.headers[ ( currentIndex + 1 ) % length ]; + break; + case keyCode.LEFT: + case keyCode.UP: + toFocus = this.headers[ ( currentIndex - 1 + length ) % length ]; + break; + case keyCode.SPACE: + case keyCode.ENTER: + this._clickHandler( { target: event.target }, event.target ); + event.preventDefault(); + } + + if ( toFocus ) { + $( event.target ).attr( "tabIndex", -1 ); + $( toFocus ).attr( "tabIndex", 0 ); + toFocus.focus(); + return false; + } + + return true; + }, + + resize: function() { + var options = this.options, + maxHeight; + + if ( options.fillSpace ) { + if ( $.browser.msie ) { + var defOverflow = this.element.parent().css( "overflow" ); + this.element.parent().css( "overflow", "hidden"); + } + maxHeight = this.element.parent().height(); + if ($.browser.msie) { + this.element.parent().css( "overflow", defOverflow ); + } + + this.headers.each(function() { + maxHeight -= $( this ).outerHeight( true ); + }); + + this.headers.next() + .each(function() { + $( this ).height( Math.max( 0, maxHeight - + $( this ).innerHeight() + $( this ).height() ) ); + }) + .css( "overflow", "auto" ); + } else if ( options.autoHeight ) { + maxHeight = 0; + this.headers.next() + .each(function() { + maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() ); + }) + .height( maxHeight ); + } + + return this; + }, + + activate: function( index ) { + // TODO this gets called on init, changing the option without an explicit call for that + this.options.active = index; + // call clickHandler with custom event + var active = this._findActive( index )[ 0 ]; + this._clickHandler( { target: active }, active ); + + return this; + }, + + _findActive: function( selector ) { + return selector + ? typeof selector === "number" + ? this.headers.filter( ":eq(" + selector + ")" ) + : this.headers.not( this.headers.not( selector ) ) + : selector === false + ? $( [] ) + : this.headers.filter( ":eq(0)" ); + }, + + // TODO isn't event.target enough? why the separate target argument? + _clickHandler: function( event, target ) { + var options = this.options; + if ( options.disabled ) { + return; + } + + // called only when using activate(false) to close all parts programmatically + if ( !event.target ) { + if ( !options.collapsible ) { + return; + } + this.active + .removeClass( "ui-state-active ui-corner-top" ) + .addClass( "ui-state-default ui-corner-all" ) + .children( ".ui-icon" ) + .removeClass( options.icons.headerSelected ) + .addClass( options.icons.header ); + this.active.next().addClass( "ui-accordion-content-active" ); + var toHide = this.active.next(), + data = { + options: options, + newHeader: $( [] ), + oldHeader: options.active, + newContent: $( [] ), + oldContent: toHide + }, + toShow = ( this.active = $( [] ) ); + this._toggle( toShow, toHide, data ); + return; + } + + // get the click target + var clicked = $( event.currentTarget || target ), + clickedIsActive = clicked[0] === this.active[0]; + + // TODO the option is changed, is that correct? + // TODO if it is correct, shouldn't that happen after determining that the click is valid? + options.active = options.collapsible && clickedIsActive ? + false : + this.headers.index( clicked ); + + // if animations are still active, or the active header is the target, ignore click + if ( this.running || ( !options.collapsible && clickedIsActive ) ) { + return; + } + + // find elements to show and hide + var active = this.active, + toShow = clicked.next(), + toHide = this.active.next(), + data = { + options: options, + newHeader: clickedIsActive && options.collapsible ? $([]) : clicked, + oldHeader: this.active, + newContent: clickedIsActive && options.collapsible ? $([]) : toShow, + oldContent: toHide + }, + down = this.headers.index( this.active[0] ) > this.headers.index( clicked[0] ); + + // when the call to ._toggle() comes after the class changes + // it causes a very odd bug in IE 8 (see #6720) + this.active = clickedIsActive ? $([]) : clicked; + this._toggle( toShow, toHide, data, clickedIsActive, down ); + + // switch classes + active + .removeClass( "ui-state-active ui-corner-top" ) + .addClass( "ui-state-default ui-corner-all" ) + .children( ".ui-icon" ) + .removeClass( options.icons.headerSelected ) + .addClass( options.icons.header ); + if ( !clickedIsActive ) { + clicked + .removeClass( "ui-state-default ui-corner-all" ) + .addClass( "ui-state-active ui-corner-top" ) + .children( ".ui-icon" ) + .removeClass( options.icons.header ) + .addClass( options.icons.headerSelected ); + clicked + .next() + .addClass( "ui-accordion-content-active" ); + } + + return; + }, + + _toggle: function( toShow, toHide, data, clickedIsActive, down ) { + var self = this, + options = self.options; + + self.toShow = toShow; + self.toHide = toHide; + self.data = data; + + var complete = function() { + if ( !self ) { + return; + } + return self._completed.apply( self, arguments ); + }; + + // trigger changestart event + self._trigger( "changestart", null, self.data ); + + // count elements to animate + self.running = toHide.size() === 0 ? toShow.size() : toHide.size(); + + if ( options.animated ) { + var animOptions = {}; + + if ( options.collapsible && clickedIsActive ) { + animOptions = { + toShow: $( [] ), + toHide: toHide, + complete: complete, + down: down, + autoHeight: options.autoHeight || options.fillSpace + }; + } else { + animOptions = { + toShow: toShow, + toHide: toHide, + complete: complete, + down: down, + autoHeight: options.autoHeight || options.fillSpace + }; + } + + if ( !options.proxied ) { + options.proxied = options.animated; + } + + if ( !options.proxiedDuration ) { + options.proxiedDuration = options.duration; + } + + options.animated = $.isFunction( options.proxied ) ? + options.proxied( animOptions ) : + options.proxied; + + options.duration = $.isFunction( options.proxiedDuration ) ? + options.proxiedDuration( animOptions ) : + options.proxiedDuration; + + var animations = $.ui.accordion.animations, + duration = options.duration, + easing = options.animated; + + if ( easing && !animations[ easing ] && !$.easing[ easing ] ) { + easing = "slide"; + } + if ( !animations[ easing ] ) { + animations[ easing ] = function( options ) { + this.slide( options, { + easing: easing, + duration: duration || 700 + }); + }; + } + + animations[ easing ]( animOptions ); + } else { + if ( options.collapsible && clickedIsActive ) { + toShow.toggle(); + } else { + toHide.hide(); + toShow.show(); + } + + complete( true ); + } + + // TODO assert that the blur and focus triggers are really necessary, remove otherwise + toHide.prev() + .attr({ + "aria-expanded": "false", + "aria-selected": "false", + tabIndex: -1 + }) + .blur(); + toShow.prev() + .attr({ + "aria-expanded": "true", + "aria-selected": "true", + tabIndex: 0 + }) + .focus(); + }, + + _completed: function( cancel ) { + this.running = cancel ? 0 : --this.running; + if ( this.running ) { + return; + } + + if ( this.options.clearStyle ) { + this.toShow.add( this.toHide ).css({ + height: "", + overflow: "" + }); + } + + // other classes are removed before the animation; this one needs to stay until completed + this.toHide.removeClass( "ui-accordion-content-active" ); + // Work around for rendering bug in IE (#5421) + if ( this.toHide.length ) { + this.toHide.parent()[0].className = this.toHide.parent()[0].className; + } + + this._trigger( "change", null, this.data ); + } +}); + +$.extend( $.ui.accordion, { + version: "1.8.17", + animations: { + slide: function( options, additions ) { + options = $.extend({ + easing: "swing", + duration: 300 + }, options, additions ); + if ( !options.toHide.size() ) { + options.toShow.animate({ + height: "show", + paddingTop: "show", + paddingBottom: "show" + }, options ); + return; + } + if ( !options.toShow.size() ) { + options.toHide.animate({ + height: "hide", + paddingTop: "hide", + paddingBottom: "hide" + }, options ); + return; + } + var overflow = options.toShow.css( "overflow" ), + percentDone = 0, + showProps = {}, + hideProps = {}, + fxAttrs = [ "height", "paddingTop", "paddingBottom" ], + originalWidth; + // fix width before calculating height of hidden element + var s = options.toShow; + originalWidth = s[0].style.width; + s.width( s.parent().width() + - parseFloat( s.css( "paddingLeft" ) ) + - parseFloat( s.css( "paddingRight" ) ) + - ( parseFloat( s.css( "borderLeftWidth" ) ) || 0 ) + - ( parseFloat( s.css( "borderRightWidth" ) ) || 0 ) ); + + $.each( fxAttrs, function( i, prop ) { + hideProps[ prop ] = "hide"; + + var parts = ( "" + $.css( options.toShow[0], prop ) ).match( /^([\d+-.]+)(.*)$/ ); + showProps[ prop ] = { + value: parts[ 1 ], + unit: parts[ 2 ] || "px" + }; + }); + options.toShow.css({ height: 0, overflow: "hidden" }).show(); + options.toHide + .filter( ":hidden" ) + .each( options.complete ) + .end() + .filter( ":visible" ) + .animate( hideProps, { + step: function( now, settings ) { + // only calculate the percent when animating height + // IE gets very inconsistent results when animating elements + // with small values, which is common for padding + if ( settings.prop == "height" ) { + percentDone = ( settings.end - settings.start === 0 ) ? 0 : + ( settings.now - settings.start ) / ( settings.end - settings.start ); + } + + options.toShow[ 0 ].style[ settings.prop ] = + ( percentDone * showProps[ settings.prop ].value ) + + showProps[ settings.prop ].unit; + }, + duration: options.duration, + easing: options.easing, + complete: function() { + if ( !options.autoHeight ) { + options.toShow.css( "height", "" ); + } + options.toShow.css({ + width: originalWidth, + overflow: overflow + }); + options.complete(); + } + }); + }, + bounceslide: function( options ) { + this.slide( options, { + easing: options.down ? "easeOutBounce" : "swing", + duration: options.down ? 1000 : 200 + }); + } + } +}); + +})( jQuery ); diff --git a/sproutcore/apps/fp/resources/jquery_ui/colorpicker.js b/sproutcore/apps/fp/resources/jquery_ui/colorpicker.js new file mode 100644 index 000000000..6f8e8fa93 --- /dev/null +++ b/sproutcore/apps/fp/resources/jquery_ui/colorpicker.js @@ -0,0 +1,1919 @@ +/*jslint devel: true, bitwise: true, regexp: true, browser: true, confusion: true, unparam: true, eqeq: true, white: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */ /*globals jQuery */ + +/* + * ColorPicker + * + * Copyright (c) 2011-2012 Martijn W. van der Lee + * Licensed under the MIT. + * + * Full-featured colorpicker for jQueryUI with full theming support. + * Most images from jPicker by Christopher T. Tillman. + * Sourcecode created from scratch by Martijn W. van der Lee. + */ + + +(function ($) { + "use strict"; + + $.colorpicker = new function() { + this.regional = []; + this.regional[''] = { + ok: 'OK', + cancel: 'Cancel', + none: 'None', + revert: 'Color', + button: 'Color', + title: 'Pick a color', + transparent: 'Transparent', + hueShort: 'H', + saturationShort:'S', + valueShort: 'V', + redShort: 'R', + greenShort: 'G', + blueShort: 'B', + alphaShort: 'A' + }; + }; + + var _container_popup = '', + + _container_inline = '
    ', + + _parts_lists = { + 'full': ['header', 'map', 'bar', 'hex', 'hsv', 'rgb', 'alpha', 'preview', 'swatches', 'footer'], + 'popup': ['map', 'bar', 'hex', 'hsv', 'rgb', 'alpha', 'preview', 'footer'], + 'inline': ['map', 'bar', 'hex', 'hsv', 'rgb', 'alpha', 'preview'] + }, + + _intToHex = function (dec) { + var result = Math.round(dec).toString(16); + if (result.length === 1) { + result = ('0' + result); + } + return result.toLowerCase(); + }, + _formats = { + 'HEX': function(color) { + return _formatColor('#rxgxbx', color); + } + , 'HEX3': function(color) { + var r = Math.round(color.r * 16); + var g = Math.round(color.g * 16); + var b = Math.round(color.b * 16); + return '#'+r.toString(16)+g.toString(16)+b.toString(16); + } + , 'RGBA': function(color) { + return _formatColor(color.a >= 1 + ? 'rgb(rd,gd,bd)' + : 'rgba(rd,gd,bd,af)', color); + } + , 'RGBA%': function(color) { + return _formatColor(color.a >= 1 + ? 'rgb(rp%,gp%,bp%)' + : 'rgba(rp%,gp%,bp%,af)', color); + } + , 'HSLA': function(color) { + return _formatColor(color.a >= 1 + ? 'hsl(hd,sd,vd)' + : 'hsla(hd,sd,vd,af)', color); + } + , 'HSLA%': function(color) { + return _formatColor(color.a >= 1 + ? 'hsl(hp%,sp%,vp%)' + : 'hsla(hp%,sp%,vp%,af)', color); + } + }, + + _formatColor = function (format, color) { + var that = this; + + var types = { + 'x': function(v) {return _intToHex(v * 255);} + , 'd': function(v) {return Math.round(v * 255);} + , 'f': function(v) {return v;} + , 'p': function(v) {return v * 100;} + }; + + if (_formats[format]) { + return _formats[format](color); + } + + return format.replace(/\\?[rgbhsva][xdfp]/g, function(m) { + if (m.match(/^\\/)) { + return m.slice(1); + } + return types[m[1]](color[m[0]]); + }); + }, + + _colors = { + 'black': [0x00, 0x00, 0x00], + 'dimgray': [0x69, 0x69, 0x69], + 'gray': [0x80, 0x80, 0x80], + 'darkgray': [0xa9, 0xa9, 0xa9], + 'silver': [0xc0, 0xc0, 0xc0], + 'lightgrey': [0xd3, 0xd3, 0xd3], + 'gainsboro': [0xdc, 0xdc, 0xdc], + 'whitesmoke': [0xf5, 0xf5, 0xf5], + 'white': [0xff, 0xff, 0xff], + 'rosybrown': [0xbc, 0x8f, 0x8f], + 'indianred': [0xcd, 0x5c, 0x5c], + 'brown': [0xa5, 0x2a, 0x2a], + 'firebrick': [0xb2, 0x22, 0x22], + 'lightcoral': [0xf0, 0x80, 0x80], + 'maroon': [0x80, 0x00, 0x00], + 'darkred': [0x8b, 0x00, 0x00], + 'red': [0xff, 0x00, 0x00], + 'snow': [0xff, 0xfa, 0xfa], + 'salmon': [0xfa, 0x80, 0x72], + 'mistyrose': [0xff, 0xe4, 0xe1], + 'tomato': [0xff, 0x63, 0x47], + 'darksalmon': [0xe9, 0x96, 0x7a], + 'orangered': [0xff, 0x45, 0x00], + 'coral': [0xff, 0x7f, 0x50], + 'lightsalmon': [0xff, 0xa0, 0x7a], + 'sienna': [0xa0, 0x52, 0x2d], + 'seashell': [0xff, 0xf5, 0xee], + 'chocolate': [0xd2, 0x69, 0x1e], + 'saddlebrown': [0x8b, 0x45, 0x13], + 'sandybrown': [0xf4, 0xa4, 0x60], + 'peachpuff': [0xff, 0xda, 0xb9], + 'peru': [0xcd, 0x85, 0x3f], + 'linen': [0xfa, 0xf0, 0xe6], + 'darkorange': [0xff, 0x8c, 0x00], + 'bisque': [0xff, 0xe4, 0xc4], + 'burlywood': [0xde, 0xb8, 0x87], + 'tan': [0xd2, 0xb4, 0x8c], + 'antiquewhite': [0xfa, 0xeb, 0xd7], + 'navajowhite': [0xff, 0xde, 0xad], + 'blanchedalmond': [0xff, 0xeb, 0xcd], + 'papayawhip': [0xff, 0xef, 0xd5], + 'orange': [0xff, 0xa5, 0x00], + 'moccasin': [0xff, 0xe4, 0xb5], + 'wheat': [0xf5, 0xde, 0xb3], + 'oldlace': [0xfd, 0xf5, 0xe6], + 'floralwhite': [0xff, 0xfa, 0xf0], + 'goldenrod': [0xda, 0xa5, 0x20], + 'darkgoldenrod': [0xb8, 0x86, 0x0b], + 'cornsilk': [0xff, 0xf8, 0xdc], + 'gold': [0xff, 0xd7, 0x00], + 'palegoldenrod': [0xee, 0xe8, 0xaa], + 'khaki': [0xf0, 0xe6, 0x8c], + 'lemonchiffon': [0xff, 0xfa, 0xcd], + 'darkkhaki': [0xbd, 0xb7, 0x6b], + 'beige': [0xf5, 0xf5, 0xdc], + 'lightgoldenrodyellow': [0xfa, 0xfa, 0xd2], + 'olive': [0x80, 0x80, 0x00], + 'yellow': [0xff, 0xff, 0x00], + 'lightyellow': [0xff, 0xff, 0xe0], + 'ivory': [0xff, 0xff, 0xf0], + 'olivedrab': [0x6b, 0x8e, 0x23], + 'yellowgreen': [0x9a, 0xcd, 0x32], + 'darkolivegreen': [0x55, 0x6b, 0x2f], + 'greenyellow': [0xad, 0xff, 0x2f], + 'lawngreen': [0x7c, 0xfc, 0x00], + 'chartreuse': [0x7f, 0xff, 0x00], + 'darkseagreen': [0x8f, 0xbc, 0x8f], + 'forestgreen': [0x22, 0x8b, 0x22], + 'limegreen': [0x32, 0xcd, 0x32], + 'lightgreen': [0x90, 0xee, 0x90], + 'palegreen': [0x98, 0xfb, 0x98], + 'darkgreen': [0x00, 0x64, 0x00], + 'green': [0x00, 0x80, 0x00], + 'lime': [0x00, 0xff, 0x00], + 'honeydew': [0xf0, 0xff, 0xf0], + 'mediumseagreen': [0x3c, 0xb3, 0x71], + 'seagreen': [0x2e, 0x8b, 0x57], + 'springgreen': [0x00, 0xff, 0x7f], + 'mintcream': [0xf5, 0xff, 0xfa], + 'mediumspringgreen': [0x00, 0xfa, 0x9a], + 'mediumaquamarine': [0x66, 0xcd, 0xaa], + 'aquamarine': [0x7f, 0xff, 0xd4], + 'turquoise': [0x40, 0xe0, 0xd0], + 'lightseagreen': [0x20, 0xb2, 0xaa], + 'mediumturquoise': [0x48, 0xd1, 0xcc], + 'darkslategray': [0x2f, 0x4f, 0x4f], + 'paleturquoise': [0xaf, 0xee, 0xee], + 'teal': [0x00, 0x80, 0x80], + 'darkcyan': [0x00, 0x8b, 0x8b], + 'darkturquoise': [0x00, 0xce, 0xd1], + 'aqua': [0x00, 0xff, 0xff], + 'cyan': [0x00, 0xff, 0xff], + 'lightcyan': [0xe0, 0xff, 0xff], + 'azure': [0xf0, 0xff, 0xff], + 'cadetblue': [0x5f, 0x9e, 0xa0], + 'powderblue': [0xb0, 0xe0, 0xe6], + 'lightblue': [0xad, 0xd8, 0xe6], + 'deepskyblue': [0x00, 0xbf, 0xff], + 'skyblue': [0x87, 0xce, 0xeb], + 'lightskyblue': [0x87, 0xce, 0xfa], + 'steelblue': [0x46, 0x82, 0xb4], + 'aliceblue': [0xf0, 0xf8, 0xff], + 'dodgerblue': [0x1e, 0x90, 0xff], + 'slategray': [0x70, 0x80, 0x90], + 'lightslategray': [0x77, 0x88, 0x99], + 'lightsteelblue': [0xb0, 0xc4, 0xde], + 'cornflowerblue': [0x64, 0x95, 0xed], + 'royalblue': [0x41, 0x69, 0xe1], + 'midnightblue': [0x19, 0x19, 0x70], + 'lavender': [0xe6, 0xe6, 0xfa], + 'navy': [0x00, 0x00, 0x80], + 'darkblue': [0x00, 0x00, 0x8b], + 'mediumblue': [0x00, 0x00, 0xcd], + 'blue': [0x00, 0x00, 0xff], + 'ghostwhite': [0xf8, 0xf8, 0xff], + 'darkslateblue': [0x48, 0x3d, 0x8b], + 'slateblue': [0x6a, 0x5a, 0xcd], + 'mediumslateblue': [0x7b, 0x68, 0xee], + 'mediumpurple': [0x93, 0x70, 0xdb], + 'blueviolet': [0x8a, 0x2b, 0xe2], + 'indigo': [0x4b, 0x00, 0x82], + 'darkorchid': [0x99, 0x32, 0xcc], + 'darkviolet': [0x94, 0x00, 0xd3], + 'mediumorchid': [0xba, 0x55, 0xd3], + 'thistle': [0xd8, 0xbf, 0xd8], + 'plum': [0xdd, 0xa0, 0xdd], + 'violet': [0xee, 0x82, 0xee], + 'purple': [0x80, 0x00, 0x80], + 'darkmagenta': [0x8b, 0x00, 0x8b], + 'magenta': [0xff, 0x00, 0xff], + 'fuchsia': [0xff, 0x00, 0xff], + 'orchid': [0xda, 0x70, 0xd6], + 'mediumvioletred': [0xc7, 0x15, 0x85], + 'deeppink': [0xff, 0x14, 0x93], + 'hotpink': [0xff, 0x69, 0xb4], + 'palevioletred': [0xdb, 0x70, 0x93], + 'lavenderblush': [0xff, 0xf0, 0xf5], + 'crimson': [0xdc, 0x14, 0x3c], + 'pink': [0xff, 0xc0, 0xcb], + 'lightpink': [0xff, 0xb6, 0xc1] + }, + + _layoutTable = function(layout, callback) { + var layout = layout.sort(function(a, b) { + if (a.pos[1] == b.pos[1]) { + return a.pos[0] - b.pos[0]; + } + return a.pos[1] - b.pos[1]; + }), + bitmap, + x, + y, + width, height, + columns, rows, + index, + cell, + html; + + // Determine dimensions of the table + width = 0; + height = 0; + $.each(layout, function(index,l) { + //for (index in layout) { // Removing this because it picks up non-indexed attributes + width = Math.max(width, layout[index].pos[0] + layout[index].pos[2]); + height = Math.max(height, layout[index].pos[1] + layout[index].pos[3]); + }); + + // Initialize bitmap + bitmap = []; + for (x = 0; x < width; ++x) { + bitmap.push(new Array(height)); + } + + // Mark rows and columns which have layout assigned + rows = new Array(height); + columns = new Array(width); + $.each(layout, function(index,l) { + //for (index in layout) { + // mark columns + for (x = 0; x < layout[index].pos[2]; x += 1) { + columns[layout[index].pos[0] + x] = true; + } + for (y = 0; y < layout[index].pos[3]; y += 1) { + rows[layout[index].pos[1] + y] = true; + } + }); + + // Generate the table + html = ''; + cell = layout[index = 0]; + for (y = 0; y < height; ++y) { + html += ''; + for (x = 0; x < width;) { + if (cell !== undefined && x == cell.pos[0] && y == cell.pos[1]) { + // Create a "real" cell + var w, + h; + + html += callback(cell, x, y); + + for (h = 0; h < cell.pos[3]; h +=1) { + for (w = 0; w < cell.pos[2]; w +=1) { + bitmap[x + w][y + h] = true; + } + } + + x += cell.pos[2]; + cell = layout[++index]; + } else { + // Fill in the gaps + var colspan = 0; + var walked = false; + + while (x < width && bitmap[x][y] === undefined && (cell === undefined || y < cell.pos[1] || (y == cell.pos[1] && x < cell.pos[0]))) { + if (columns[x] === true) { + colspan += 1; + } + walked = true; + x += 1; + } + + if (colspan > 0) { + html += ''; + } else if (!walked) { + x += 1; + } + } + } + html += ''; + } + + return '' + html + '
    '; + }; + + $.widget("vanderlee.colorpicker", { + options: { + alpha: false, // Show alpha controls and mode + altAlpha: true, // change opacity of altField as well? + altField: '', // selector for DOM elements which change background color on change. + altOnChange: true, // true to update on each change, false to update only on close. + altProperties: 'background-color', // comma separated list of any of 'background-color', 'color', 'border-color', 'outline-color' + autoOpen: false, // Open dialog automatically upon creation + buttonColorize: false, + buttonImage: '/img/colorpicker/ui-colorpicker.png', + buttonImageOnly: true, + buttonText: null, // Text on the button and/or title of button image. + closeOnEscape: true, // Close the dialog when the escape key is pressed. + closeOnOutside: true, // Close the dialog when clicking outside the dialog (not for inline) + color: '#00FF00', // Initial color (for inline only) + colorFormat: 'HEX', // Format string for output color format + duration: 'fast', + hsv: true, // Show HSV controls and modes + regional: '', + layout: { + map: [0, 0, 1, 5], // Left, Top, Width, Height (in table cells). + bar: [1, 0, 1, 5], + preview: [2, 0, 1, 1], + hsv: [2, 1, 1, 1], + rgb: [2, 2, 1, 1], + alpha: [2, 3, 1, 1], + hex: [2, 4, 1, 1], + swatches: [3, 0, 1, 5] + }, + limit: '', // Limit color "resolution": '', 'websafe', 'nibble', 'binary' + mode: 'h', // Initial editing mode, h, s, v, r, g, b or a + parts: '', // leave empty for automatic selection + rgb: true, // Show RGB controls and modes + showAnim: 'fadeIn', + showNoneButton: false, + showOn: 'focus', // 'focus', 'button', 'both' + showOptions: {}, + swatches: null, + title: null, + zIndex: null, + + close: null, + select: null + }, + + _create: function () { + var that = this; + + that.widgetEventPrefix = 'color'; + + that.opened = false; + that.generated = false; + that.inline = false; + that.changed = false; + + that.dialog = null; + that.button = null; + that.image = null; + + that.mode = that.options.mode; + + if (that.options.swatches === null) { + that.options.swatches = _colors; + } + + if (this.element[0].nodeName.toLowerCase() === 'input') { + that._setColor(that.element.val()); + + $('body').append(_container_popup); + that.dialog = $('.ui-colorpicker:last'); + + // Click outside/inside + $(document).mousedown(function (event) { + if (!that.opened || event.target === that.element[0]) { + return; + } + + // Check if clicked on any part of dialog + if ($(event.target).parents('.ui-colorpicker').length > 0) { + that.element.blur(); // inside window! + return; + } + + // Check if clicked on button + var p, + parents = $(event.target).parents(); + for (p in parents) { + if (that.button !== null && parents[p] === that.button[0]) { + return; + } + } + + // no closeOnOutside + if (!that.options.closeOnOutside) { + return; + } + + that.close(); + }); + + $(document).keydown(function (event) { + if (event.keyCode == 27 && that.opened && that.options.closeOnEscape) { + that.close(); + } + }); + + if (that.options.showOn === 'focus' || that.options.showOn === 'both') { + that.element.focus(function () { + that.open(); + }); + } + if (that.options.showOn === 'button' || that.options.showOn === 'both') { + if (that.options.buttonImage !== '') { + var text = that.options.buttonText ? that.options.buttonText : that._getRegional('button'); + + that.image = $('').attr({ + 'src': that.options.buttonImage, + 'alt': text, + 'title': text + }); + + that._setImageBackground(); + } + + if (that.options.buttonImageOnly && that.image) { + that.button = that.image; + } else { + that.button = $('').html(that.image || that.options.buttonText).button(); + that.image = that.image ? $('img', that.button).first() : null; + } + that.button.insertAfter(that.element).click(function () { + that[that.opened ? 'close' : 'open'](); + }); + } + + if (that.options.autoOpen) { + that.open(); + } + + that.element.keydown(function (event) { + if (event.keyCode === 9) { + that.close(); + } + }).keyup(function (event) { + var rgb = that._parseHex(that.element.val()); + if (rgb) { + that.color = (rgb === false ? new that.Color() : new that.Color(rgb[0], rgb[1], rgb[2])); + that._change(); + } + }); + } else { + that.inline = true; + + $(this.element).html(_container_inline); + that.dialog = $('.ui-colorpicker', this.element); + + that._generate(); + + that.opened = true; + } + + return this; + }, + + destroy: function() { + this.element.unbind(); + + if (this.image !== null) { + this.image.remove(); + } + + if (this.button !== null) { + this.button.remove(); + } + + if (this.dialog !== null) { + this.dialog.remove(); + } + }, + + _setOption: function(key, value){ + var that = this; + + switch (key) { + case "disabled": + if (value) { + that.dialog.addClass('ui-colorpicker-disabled'); + } else { + that.dialog.removeClass('ui-colorpicker-disabled'); + } + break; + } + + $.Widget.prototype._setOption.apply(that, arguments); + }, + + /** + * If an alternate field is specified, set it according to the current color. + */ + _setAltField: function () { + if (this.options.altOnChange && this.options.altField && this.options.altProperties) { + var index, + property, + properties = this.options.altProperties.split(','); + + for (index in properties) { + property = $.trim(properties[index]); + switch (property) { + case 'color': + case 'background-color': + case 'outline-color': + case 'border-color': + $(this.options.altField).css(property, this.color.set? this.color.toCSS() : ''); + break; + } + } + + if (this.options.altAlpha) { + $(this.options.altField).css('opacity', this.color.set? this.color.a : ''); + } + } + }, + + _setColor: function(text) { + var rgb = this._parseColor(text); + this.color = (rgb === false ? new this.Color() : new this.Color(rgb[0], rgb[1], rgb[2], rgb[3])); + this.currentColor = $.extend({}, this.color); + + //@todo only on generate and create + this._setImageBackground(); + this._setAltField(); + }, + + setColor: function(text) { + this._setColor(text); + this._change(this.color.set); + }, + + _generate: function () { + var that = this, + index, + part, + parts_list; + + // Set color based on element? + that._setColor(that.inline? that.options.color : that.element.val()); + + // Determine the parts to include in this colorpicker + if (typeof that.options.parts === 'string') { + if (that.options.parts in _parts_lists) { + parts_list = _parts_lists[that.options.parts]; + } else { + // automatic + parts_list = _parts_lists[that.inline ? 'inline' : 'popup']; + } + } else { + parts_list = that.options.parts; + } + + // Add any parts to the internal parts list + that.parts = {}; + for (index in parts_list) { + part = parts_list[index]; + if (part in that._parts) { + that.parts[part] = new that._parts[part](that); + } + } + + if (!that.generated) { + var layout_parts = []; + + $.each(that.options.layout, function(index,l) { + //for (index in that.options.layout) { + if (index in that.parts) { + layout_parts.push({ + part: index, + pos: that.options.layout[index] + }); + } + }); + + $(_layoutTable(layout_parts, function(cell, x, y) { + var classes = []; + + if (x > 0) { + classes.push('ui-colorpicker-padding-left'); + } + + if (y > 0) { + classes.push('ui-colorpicker-padding-top'); + } + + return ' 1 ? ' colspan="' + cell.pos[2] + '"' : '') + + (cell.pos[3] > 1 ? ' rowspan="' + cell.pos[3] + '"' : '') + + (classes.length > 0 ? ' class="' + classes.join(' ') + '"' : '') + + ' valign="top">'; + })).appendTo(that.dialog).addClass('ui-dialog-content ui-widget-content'); + + that._initAllParts(); + that._generateAllParts(); + that.generated = true; + } + }, + + _effectGeneric: function (show, slide, fade, callback) { + var that = this; + + if ($.effects && $.effects[that.options.showAnim]) { + that.dialog[show](that.options.showAnim, that.options.showOptions, that.options.duration, callback); + } else { + that.dialog[(that.options.showAnim === 'slideDown' ? + slide + : (that.options.showAnim === 'fadeIn' ? + fade + : show))]((that.options.showAnim ? that.options.duration : null), callback); + if (!that.options.showAnim || !that.options.duration) { + callback(); + } + } + }, + + _effectShow: function (callback) { + this._effectGeneric('show', 'slideDown', 'fadeIn', callback); + }, + + _effectHide: function (callback) { + this._effectGeneric('hide', 'slideUp', 'fadeOut', callback); + }, + + open: function () { + if (!this.opened) { + if (this.options.zIndex) { + this.dialog.css('z-index', this.options.zIndex); + } + + this._generate(); + + var offset = this.element.offset(), + x = offset.left, + y = offset.top + this.element.outerHeight(); + x -= Math.max(0, (x + this.dialog.width()) - $(window).width() + 20); + y -= Math.max(0, (y + this.dialog.height()) - $(window).height() + 20); + this.dialog.css({'left': x, 'top': y}); + + this._effectShow(); + this.opened = true; + + var that = this; + // Without waiting for domready the width of the map is 0 and we + // wind up with the cursor stuck in the upper left corner + $(function() { + $.each(that.parts, function (index, part) { + part.repaint(); + }); + }); + } + }, + + _setImageBackground: function() { + if (this.image && this.options.buttonColorize) { + this.image.css('background-color', this.color.set? this.color.toCSS() : ''); + } + }, + + close: function () { + var that = this; + + this.currentColor = $.extend({}, this.color); + this.changed = false; + + // tear down the interface + this._effectHide(function () { + if (that.options.zIndex) { + that.dialog.css('z-index', ''); + } + + that.dialog.empty(); + that.generated = false; + + that.opened = false; + that._callback('close'); + }); + }, + + _callback: function (callback) { + var that = this; + + if (that.color.set) { + that._trigger(callback, null, { + formatted: _formatColor(that.options.colorFormat, that.color), + r: that.color.r + , g: that.color.g + , b: that.color.b + , a: that.color.a + , h: that.color.h + , s: that.color.s + , v: that.color.v + }); + } else { + that._trigger(callback, null, { + formatted: '' + }); + } + }, + + _generateAllParts: function () { + $.each(this.parts, function (index, part) { + part.generate(); + }); + }, + + _initAllParts: function () { + $.each(this.parts, function (index, part) { + part.init(); + }); + }, + + _change: function (set /* = true */) { + this.color.set = (set !== false); + + this.changed = true; + + // Limit color palette + switch (this.options.limit) { + case 'websafe': + this.color.limit(6); + break; + + case 'nibble': + this.color.limit(16); + break; + + case 'binary': + this.color.limit(2); + break; + } + + // update colors + if (!this.inline) { + if (!this.color.set) { + this.element.val(''); + } else if(!this.color.equals(this._parseHex(this.element.val()))) { + // Fix by Andy Likuski to format the input element value to the desired format + this.element.val(_formatColor(this.options.colorFormat, this.color)); + // former code which ignores formatting choice + //this.element.val(this.color.toHex()); + } + + this._setImageBackground(); + this._setAltField(); + } + + if (this.opened) { + $.each(this.parts, function (index, part) { + part.repaint(); + }); + } + + // callback + this._callback('select'); + }, + + // This will be deprecated by jQueryUI 1.9 widget + _hoverable: function (e) { + e.hover(function () { + e.addClass("ui-state-hover"); + }, function () { + e.removeClass("ui-state-hover"); + }); + }, + + // This will be deprecated by jQueryUI 1.9 widget + _focusable: function (e) { + e.focus(function () { + e.addClass("ui-state-focus"); + }).blur(function () { + e.removeClass("ui-state-focus"); + }); + }, + + _getRegional: function(name) { + return $.colorpicker.regional[this.options.regional][name] !== undefined ? + $.colorpicker.regional[this.options.regional][name] : $.colorpicker.regional[''][name]; + }, + + _parts: { + header: function (inst) { + var that = this, + e = null, + _html; + + _html = function () { + var title = inst.options.title ? inst.options.title : inst._getRegional('title'); + + return '
    ' + + '' + title + '' + + '' + + 'close
    '; + }; + + this.init = function () { + e = $(_html()).prependTo(inst.dialog); + var close = $('.ui-dialog-titlebar-close', e); + inst._hoverable(close); + inst._focusable(close); + + close.click( function() { + inst.close() + }); + }; + + this.repaint = function () { + }; + + this.generate = function () { + this.repaint(); + }; + }, + + map: function (inst) { + var that = this, + e = null, + _mousedown, _mouseup, _mousemove, _html; + + _mousedown = function (event) { + if (!inst.opened) { + return; + } + + var div = $('#ui-colorpicker-map-layer-pointer', e), + offset = div.offset(), + width = div.width(), + height = div.height(), + x = event.pageX - offset.left, + y = event.pageY - offset.top; + + if (x >= 0 && x < width && y >= 0 && y < height) { + event.stopImmediatePropagation(); + event.preventDefault(); + $(document).unbind('mousedown', _mousedown); + $(document).bind('mouseup', _mouseup); + $(document).bind('mousemove', _mousemove); + _mousemove(event); + } + }; + + _mouseup = function (event) { + event.stopImmediatePropagation(); + event.preventDefault(); + $(document).unbind('mouseup', _mouseup); + $(document).unbind('mousemove', _mousemove); + $(document).bind('mousedown', _mousedown); + }; + + _mousemove = function (event) { + event.stopImmediatePropagation(); + event.preventDefault(); + + if (event.pageX === that.x && event.pageY === that.y) { + return; + } + that.x = event.pageX; + that.y = event.pageY; + + var div = $('#ui-colorpicker-map-layer-pointer', e), + offset = div.offset(), + width = div.width(), + height = div.height(), + x = event.pageX - offset.left, + y = event.pageY - offset.top; + + x = Math.max(0, Math.min(x / width, 1)); + y = Math.max(0, Math.min(y / height, 1)); + + // interpret values + switch (inst.mode) { + case 'h': + inst.color.s = x; + inst.color.v = 1 - y; + inst.color.updateRGB(); + break; + + case 's': + case 'a': + inst.color.h = x; + inst.color.v = 1 - y; + inst.color.updateRGB(); + break; + + case 'v': + inst.color.h = x; + inst.color.s = 1 - y; + inst.color.updateRGB(); + break; + + case 'r': + inst.color.b = x; + inst.color.g = 1 - y; + inst.color.updateHSV(); + break; + + case 'g': + inst.color.b = x; + inst.color.r = 1 - y; + inst.color.updateHSV(); + break; + + case 'b': + inst.color.r = x; + inst.color.g = 1 - y; + inst.color.updateHSV(); + break; + } + + inst._change(); + }; + + _html = function () { + var html = '
    ' + + ' ' + + ' ' + + (inst.options.alpha ? ' ' : '') + + '
    '; + return html; + }; + + this.generate = function () { + switch (inst.mode) { + case 'h': + $('#ui-colorpicker-map-layer-1', e).css({'background-position': '0 0', 'opacity': ''}).show(); + $('#ui-colorpicker-map-layer-2', e).hide(); + break; + + case 's': + case 'a': + $('#ui-colorpicker-map-layer-1', e).css({'background-position': '0 -260px', 'opacity': ''}).show(); + $('#ui-colorpicker-map-layer-2', e).css({'background-position': '0 -520px', 'opacity': ''}).show(); + break; + + case 'v': + $(e).css('background-color', 'black'); + $('#ui-colorpicker-map-layer-1', e).css({'background-position': '0 -780px', 'opacity': ''}).show(); + $('#ui-colorpicker-map-layer-2', e).hide(); + break; + + case 'r': + $('#ui-colorpicker-map-layer-1', e).css({'background-position': '0 -1040px', 'opacity': ''}).show(); + $('#ui-colorpicker-map-layer-2', e).css({'background-position': '0 -1300px', 'opacity': ''}).show(); + break; + + case 'g': + $('#ui-colorpicker-map-layer-1', e).css({'background-position': '0 -1560px', 'opacity': ''}).show(); + $('#ui-colorpicker-map-layer-2', e).css({'background-position': '0 -1820px', 'opacity': ''}).show(); + break; + + case 'b': + $('#ui-colorpicker-map-layer-1', e).css({'background-position': '0 -2080px', 'opacity': ''}).show(); + $('#ui-colorpicker-map-layer-2', e).css({'background-position': '0 -2340px', 'opacity': ''}).show(); + break; + } + that.repaint(); + }; + + this.repaint = function () { + var div = $('#ui-colorpicker-map-layer-pointer', e), + x = 0, + y = 0; + + switch (inst.mode) { + case 'h': + x = inst.color.s * div.width(); + y = (1 - inst.color.v) * div.width(); + $(e).css('background-color', inst.color.copy().normalize().toCSS()); + break; + + case 's': + case 'a': + x = inst.color.h * div.width(); + y = (1 - inst.color.v) * div.width(); + $('#ui-colorpicker-map-layer-2', e).css('opacity', 1 - inst.color.s); + break; + + case 'v': + x = inst.color.h * div.width(); + y = (1 - inst.color.s) * div.width(); + $('#ui-colorpicker-map-layer-1', e).css('opacity', inst.color.v); + break; + + case 'r': + x = inst.color.b * div.width(); + y = (1 - inst.color.g) * div.width(); + $('#ui-colorpicker-map-layer-2', e).css('opacity', inst.color.r); + break; + + case 'g': + x = inst.color.b * div.width(); + y = (1 - inst.color.r) * div.width(); + $('#ui-colorpicker-map-layer-2', e).css('opacity', inst.color.g); + break; + + case 'b': + x = inst.color.r * div.width(); + y = (1 - inst.color.g) * div.width(); + $('#ui-colorpicker-map-layer-2', e).css('opacity', inst.color.b); + break; + } + + if (inst.options.alpha) { + $('#ui-colorpicker-map-layer-alpha', e).css('opacity', 1 - inst.color.a); + } + + $('#ui-colorpicker-map-pointer', e).css({ + 'left': x - 7, + 'top': y - 7 + }); + }; + + this.init = function () { + e = $(_html()).appendTo($('#ui-colorpicker-map-container', inst.dialog)); + + e.bind('mousedown', _mousedown); + }; + }, + + bar: function (inst) { + var that = this, + e = null, + _mousedown, _mouseup, _mousemove, _html; + + _mousedown = function (event) { + if (!inst.opened) { + return; + } + + var div = $('#ui-colorpicker-bar-layer-pointer', e), + offset = div.offset(), + width = div.width(), + height = div.height(), + x = event.pageX - offset.left, + y = event.pageY - offset.top; + + if (x >= 0 && x < width && y >= 0 && y < height) { + event.stopImmediatePropagation(); + event.preventDefault(); + $(document).unbind('mousedown', _mousedown); + $(document).bind('mouseup', _mouseup); + $(document).bind('mousemove', _mousemove); + _mousemove(event); + } + }; + + _mouseup = function (event) { + event.stopImmediatePropagation(); + event.preventDefault(); + $(document).unbind('mouseup', _mouseup); + $(document).unbind('mousemove', _mousemove); + $(document).bind('mousedown', _mousedown); + }; + + _mousemove = function (event) { + event.stopImmediatePropagation(); + event.preventDefault(); + + if (event.pageY === that.y) { + return; + } + that.y = event.pageY; + + var div = $('#ui-colorpicker-bar-layer-pointer', e), + offset = div.offset(), + height = div.height(), + y = event.pageY - offset.top; + + y = Math.max(0, Math.min(y / height, 1)); + + // interpret values + switch (inst.mode) { + case 'h': + inst.color.h = 1 - y; + inst.color.updateRGB(); + break; + + case 's': + inst.color.s = 1 - y; + inst.color.updateRGB(); + break; + + case 'v': + inst.color.v = 1 - y; + inst.color.updateRGB(); + break; + + case 'r': + inst.color.r = 1 - y; + inst.color.updateHSV(); + break; + + case 'g': + inst.color.g = 1 - y; + inst.color.updateHSV(); + break; + + case 'b': + inst.color.b = 1 - y; + inst.color.updateHSV(); + break; + + case 'a': + inst.color.a = 1 - y; + break; + } + + inst._change(); + }; + + _html = function () { + var html = '
    ' + + ' ' + + ' ' + + ' ' + + ' '; + + if (inst.options.alpha) { + html += ' ' + + ' '; + } + + html += '
    '; + + return html; + }; + + this.generate = function () { + switch (inst.mode) { + case 'h': + case 's': + case 'v': + case 'r': + case 'g': + case 'b': + $('#ui-colorpicker-bar-layer-alpha', e).show(); + $('#ui-colorpicker-bar-layer-alphabar', e).hide(); + break; + + case 'a': + $('#ui-colorpicker-bar-layer-alpha', e).hide(); + $('#ui-colorpicker-bar-layer-alphabar', e).show(); + break; + } + + switch (inst.mode) { + case 'h': + $('#ui-colorpicker-bar-layer-1', e).css({'background-position': '0 0', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-2', e).hide(); + $('#ui-colorpicker-bar-layer-3', e).hide(); + $('#ui-colorpicker-bar-layer-4', e).hide(); + break; + + case 's': + $('#ui-colorpicker-bar-layer-1', e).css({'background-position': '0 -260px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-2', e).css({'background-position': '0 -520px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-3', e).hide(); + $('#ui-colorpicker-bar-layer-4', e).hide(); + break; + + case 'v': + $('#ui-colorpicker-bar-layer-1', e).css({'background-position': '0 -520px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-2', e).hide(); + $('#ui-colorpicker-bar-layer-3', e).hide(); + $('#ui-colorpicker-bar-layer-4', e).hide(); + break; + + case 'r': + $('#ui-colorpicker-bar-layer-1', e).css({'background-position': '0 -1560px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-2', e).css({'background-position': '0 -1300px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-3', e).css({'background-position': '0 -780px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-4', e).css({'background-position': '0 -1040px', 'opacity': ''}).show(); + break; + + case 'g': + $('#ui-colorpicker-bar-layer-1', e).css({'background-position': '0 -2600px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-2', e).css({'background-position': '0 -2340px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-3', e).css({'background-position': '0 -1820px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-4', e).css({'background-position': '0 -2080px', 'opacity': ''}).show(); + break; + + case 'b': + $('#ui-colorpicker-bar-layer-1', e).css({'background-position': '0 -3640px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-2', e).css({'background-position': '0 -3380px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-3', e).css({'background-position': '0 -2860px', 'opacity': ''}).show(); + $('#ui-colorpicker-bar-layer-4', e).css({'background-position': '0 -3120px', 'opacity': ''}).show(); + break; + + case 'a': + $('#ui-colorpicker-bar-layer-1', e).hide(); + $('#ui-colorpicker-bar-layer-2', e).hide(); + $('#ui-colorpicker-bar-layer-3', e).hide(); + $('#ui-colorpicker-bar-layer-4', e).hide(); + break; + } + that.repaint(); + }; + + this.repaint = function () { + var div = $('#ui-colorpicker-bar-layer-pointer', e), + y = 0; + + switch (inst.mode) { + case 'h': + y = (1 - inst.color.h) * div.height(); + break; + + case 's': + y = (1 - inst.color.s) * div.height(); + $('#ui-colorpicker-bar-layer-2', e).css('opacity', 1 - inst.color.v); + $(e).css('background-color', inst.color.copy().normalize().toCSS()); + break; + + case 'v': + y = (1 - inst.color.v) * div.height(); + $(e).css('background-color', inst.color.copy().normalize().toCSS()); + break; + + case 'r': + y = (1 - inst.color.r) * div.height(); + $('#ui-colorpicker-bar-layer-2', e).css('opacity', Math.max(0, (inst.color.b - inst.color.g))); + $('#ui-colorpicker-bar-layer-3', e).css('opacity', Math.max(0, (inst.color.g - inst.color.b))); + $('#ui-colorpicker-bar-layer-4', e).css('opacity', Math.min(inst.color.b, inst.color.g)); + break; + + case 'g': + y = (1 - inst.color.g) * div.height(); + $('#ui-colorpicker-bar-layer-2', e).css('opacity', Math.max(0, (inst.color.b - inst.color.r))); + $('#ui-colorpicker-bar-layer-3', e).css('opacity', Math.max(0, (inst.color.r - inst.color.b))); + $('#ui-colorpicker-bar-layer-4', e).css('opacity', Math.min(inst.color.r, inst.color.b)); + break; + + case 'b': + y = (1 - inst.color.b) * div.height(); + $('#ui-colorpicker-bar-layer-2', e).css('opacity', Math.max(0, (inst.color.r - inst.color.g))); + $('#ui-colorpicker-bar-layer-3', e).css('opacity', Math.max(0, (inst.color.g - inst.color.r))); + $('#ui-colorpicker-bar-layer-4', e).css('opacity', Math.min(inst.color.r, inst.color.g)); + break; + + case 'a': + y = (1 - inst.color.a) * div.height(); + $(e).css('background-color', inst.color.copy().normalize().toCSS()); + break; + } + + if (inst.mode !== 'a') { + $('#ui-colorpicker-bar-layer-alpha', e).css('opacity', 1 - inst.color.a); + } + + $('#ui-colorpicker-bar-pointer', e).css('top', y - 3); + }; + + this.init = function () { + e = $(_html()).appendTo($('#ui-colorpicker-bar-container', inst.dialog)); + + e.bind('mousedown', _mousedown); + }; + }, + + hsv: function (inst) { + var that = this, + e = null, + _html; + + _html = function () { + var html = ''; + + if (inst.options.hsv) { + html += '
    °
    ' + + '
    %
    ' + + '
    %
    '; + } + + return '
    ' + html + '
    '; + }; + + this.init = function () { + e = $(_html()).appendTo($('#ui-colorpicker-hsv-container', inst.dialog)); + + $('.ui-colorpicker-mode', e).click(function () { + inst.mode = $(this).val(); + inst._generateAllParts(); + }); + + $('.ui-colorpicker-number', e).bind('change input keyup', function () { + inst.color.h = $('#ui-colorpicker-h .ui-colorpicker-number', e).val() / 360; + inst.color.s = $('#ui-colorpicker-s .ui-colorpicker-number', e).val() / 100; + inst.color.v = $('#ui-colorpicker-v .ui-colorpicker-number', e).val() / 100; + + inst.color.updateRGB(); + + inst._change(); + }); + }; + + this.repaint = function () { + var c = $.extend(inst.color); + c.h *= 360; + c.s *= 100; + c.v *= 100; + + $.each(c, function (index, value) { + var v = Math.round(value); + if (!$('#ui-colorpicker-' + index + ' .ui-colorpicker-number', e).is(':focus') + && $('#ui-colorpicker-' + index + ' .ui-colorpicker-number', e).val() !== v) { + $('#ui-colorpicker-' + index + ' .ui-colorpicker-number', e).val(v); + } + }); + }; + + this.generate = function () { + $('.ui-colorpicker-mode', e).each(function () { + $(this).attr('checked', $(this).val() === inst.mode); + }); + this.repaint(); + }; + }, + + rgb: function (inst) { + var that = this, + e = null, + _html; + + _html = function () { + var html = ''; + + if (inst.options.rgb) { + html += '
    ' + + '
    ' + + '
    '; + } + + return '
    ' + html + '
    '; + }; + + this.init = function () { + e = $(_html()).appendTo($('#ui-colorpicker-rgb-container', inst.dialog)); + + $('.ui-colorpicker-mode', e).click(function () { + inst.mode = $(this).val(); + inst._generateAllParts(); + }); + + $('.ui-colorpicker-number', e).bind('change input keyup', function () { + inst.color.r = $('#ui-colorpicker-r .ui-colorpicker-number', e).val() / 255; + inst.color.g = $('#ui-colorpicker-g .ui-colorpicker-number', e).val() / 255; + inst.color.b = $('#ui-colorpicker-b .ui-colorpicker-number', e).val() / 255; + + inst.color.updateHSV(); + + inst._change(); + }); + }; + + this.repaint = function () { + var c = $.extend(inst.color); + c.r *= 255; + c.g *= 255; + c.b *= 255; + + $.each(c, function (index, value) { + var v = Math.round(value); + if (!$('#ui-colorpicker-' + index + ' .ui-colorpicker-number', e).is(':focus') + && $('#ui-colorpicker-' + index + ' .ui-colorpicker-number', e).val() !== v) { + $('#ui-colorpicker-' + index + ' .ui-colorpicker-number', e).val(v); + } + }); + }; + + this.generate = function () { + $('.ui-colorpicker-mode', e).each(function () { + $(this).attr('checked', $(this).val() === inst.mode); + }); + this.repaint(); + }; + }, + + + alpha: function (inst) { + var that = this, + e = null, + _html; + + _html = function () { + var html = ''; + + if (inst.options.alpha) { + html += '
    %
    '; + } + + return '
    ' + html + '
    '; + }; + + this.init = function () { + e = $(_html()).appendTo($('#ui-colorpicker-alpha-container', inst.dialog)); + + $('.ui-colorpicker-mode', e).click(function () { + inst.mode = $(this).val(); + inst._generateAllParts(); + }); + + $('.ui-colorpicker-number', e).bind('change input keyup', function () { + inst.color.a = $('#ui-colorpicker-a .ui-colorpicker-number', e).val() / 100; + + inst._change(); + }); + }; + + this.repaint = function () { + var c = $.extend(inst.color); + c.a *= 100; + + $.each(c, function (index, value) { + var v = Math.round(value); + if (!$('#ui-colorpicker-' + index + ' .ui-colorpicker-number', e).is(':focus') + && $('#ui-colorpicker-' + index + ' .ui-colorpicker-number', e).val() !== v) { + $('#ui-colorpicker-' + index + ' .ui-colorpicker-number', e).val(v); + } + }); + }; + + this.generate = function () { + $('.ui-colorpicker-mode', e).each(function () { + $(this).attr('checked', $(this).val() === inst.mode); + }); + this.repaint(); + }; + }, + + preview: function (inst) { + var that = this, + e = null, + _html; + + _html = function () { + return '
    ' + + '' + + '
    ' + + '
    ' + + '
    ' + + '
    '; + }; + + this.init = function () { + e = $(_html()).appendTo($('#ui-colorpicker-preview-container', inst.dialog)); + + $('#ui-colorpicker-preview-initial', e).click(function () { + inst.color = $.extend({}, inst.currentColor); + inst._change(); + }); + + }; + + this.repaint = function () { + $('#ui-colorpicker-preview-initial', e).css('background-color', inst.currentColor.toCSS()).attr('title', inst.currentColor.toHex()); + $('#ui-colorpicker-preview-initial-alpha', e).css('opacity', 1 - inst.currentColor.a); + $('#ui-colorpicker-preview-current', e).css('background-color', inst.color.toCSS()).attr('title', inst.color.toHex()); + $('#ui-colorpicker-preview-current-alpha', e).css('opacity', 1 - inst.color.a); + }; + + this.generate = function () { + if (inst.options.alpha) { + $('#ui-colorpicker-preview-initial-alpha, #ui-colorpicker-preview-current-alpha', e).show(); + } else { + $('#ui-colorpicker-preview-initial-alpha, #ui-colorpicker-preview-current-alpha', e).hide(); + } + + this.repaint(); + }; + }, + + hex: function (inst) { + var that = this, + e = null, + _html; + + _html = function () { + var html = ''; + + if (inst.options.alpha) { + html += ''; + } + + html += ''; + + return '
    ' + html + '
    '; + }; + + this.init = function () { + e = $(_html()).appendTo($('#ui-colorpicker-hex-container', inst.dialog)); + + $('#ui-colorpicker-hex-input', e).bind('change keyup', function () { + var rgb = inst._parseHex($(this).val()); + inst.color.r = rgb[0]; + inst.color.g = rgb[1]; + inst.color.b = rgb[2]; + inst.color.updateHSV(); + inst._change(); + }); + + $('#ui-colorpicker-hex-alpha', e).bind('change keyup', function () { + inst.color.a = parseInt($('#ui-colorpicker-hex-alpha', e).val(), 16); + inst._change(); + }); + }; + + this.repaint = function () { + if (!$('#ui-colorpicker-hex-input', e).is(':focus')) { + $('#ui-colorpicker-hex-input', e).val(inst.color.toHex(true)); + } + + if (!$('#ui-colorpicker-hex-alpha', e).is(':focus')) { + $('#ui-colorpicker-hex-alpha', e).val(_intToHex(inst.color.a * 255)); + } + }; + + this.generate = function () { + this.repaint(); + }; + }, + + swatches: function (inst) { + var that = this, + e = null, + _html; + + _html = function () { + var html = ''; + + $.each(inst.options.swatches, function (name, color) { + var hex = _intToHex(color[0]) + _intToHex(color[1]) + _intToHex(color[2]); // @todo use formatter + html += '
    '; + }); + + return '
    ' + html + '
    '; + }; + + this.init = function () { + e = $(_html()).appendTo($('#ui-colorpicker-swatches-container', inst.dialog)); + + $('.ui-colorpicker-swatch', e).click(function () { + var rgb = inst._parseColor($(this).css('background-color')); + inst.color = (rgb === false ? new inst.Color() : new inst.Color(rgb[0], rgb[1], rgb[2], rgb[3])); + inst._change(); + }); + }; + + this.repaint = function () { + // Not affected by changing color; + }; + + this.generate = function () { + // Not affected by changing color; + }; + }, + + footer: function (inst) { + var that = this, + e = null, + _html; + + _html = function () { + var html = ''; + + if (inst.options.alpha || (!inst.inline && inst.options.showNoneButton)) { + html += '
    '; + + if (inst.options.alpha) { +// html += ''; + html += ''; + } + if (!inst.inline && inst.options.showNoneButton) { + html += ''; + } + html += '
    '; + } + + if (!inst.inline) { + html += '
    '; + html += ''; + html += ''; + html += '
    '; + } + + return '
    ' + html + '
    '; + }; + + this.init = function () { + e = $(_html()).appendTo(inst.dialog); + + $('.ui-colorpicker-ok', e).button().click(function () { + inst.close(); + }); + + $('.ui-colorpicker-cancel', e).button().click(function () { + inst.color = $.extend({}, inst.currentColor); + inst._change(inst.color.set); + inst.close(); + }); + + $('#ui-colorpicker-special-transparent', e).button({ + label: inst._getRegional('transparent') + }); + + //inst._getRegional('transparent') + $('.ui-colorpicker-buttonset', e).buttonset(); + + $('#ui-colorpicker-special-color', e).click(function () { + inst._change(); + }); + + $('#ui-colorpicker-special-none', e).click(function () { + inst._change(false); + }); + + $('#ui-colorpicker-special-transparent', e).click(function () { + inst.color.a = 0; + inst._change(); + }); + }; + + this.repaint = function () { + if (!inst.color.set) { + $('#ui-colorpicker-special-none', e).attr('checked', true).button( "refresh" ); + } else if (inst.color.a == 0) { + $('#ui-colorpicker-special-transparent', e).attr('checked', true).button( "refresh" ); + } else { + $('input', e).attr('checked', false).button( "refresh" ); + } + + $('.ui-colorpicker-cancel', e).button(inst.changed ? 'enable' : 'disable'); + }; + + this.generate = function () {}; + } + }, + + _parseHex: function (color) { + var name = $.trim(color).toLowerCase(), + c, + m; + + if (_colors[name]) { + c = _colors[name]; + return [c[0] / 255, c[1] / 255, c[2] / 255]; + } + + // {#}rrggbb + m = /^#?([a-fA-F0-9]{1,6})/.exec(color); + if (m) { + c = parseInt(m[1], 16); + return [((c >> 16) & 0xFF) / 255, + ((c >> 8) & 0xFF) / 255, + (c & 0xFF) / 255]; + } + + return false; + }, + + _parseColor: function (color) { + var m; + + if (color == '') { + return false; + } + + // rgba(r,g,b,a) + m = /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/.exec(color); + if (m) { + return [ + m[1] / 255, + m[2] / 255, + m[3] / 255, + parseFloat(m[4]) + ]; + } + + // rgba(r%,g%,b%,a%) + m = /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d+(?:\.\d+)?)\s*)?\)/.exec(color); + if (m) { + return [ + m[1] / 100, + m[2] / 100, + m[3] / 100, + m[4] / 100 + ]; + } + + // #rrggbb + m = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color); + if (m) { + return [ + parseInt(m[1], 16) / 255, + parseInt(m[2], 16) / 255, + parseInt(m[3], 16) / 255 + ]; + } + + // #rgb + m = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color); + if (m) { + return [ + parseInt(m[1] + m[1], 16) / 255, + parseInt(m[2] + m[2], 16) / 255, + parseInt(m[3] + m[3], 16) / 255 + ]; + } + + return this._parseHex(color); + }, + + Color: function () { + var arg, + args = arguments; + + this.updateRGB = function () { + this.h = Math.max(0, Math.min(this.h, 1)); + this.s = Math.max(0, Math.min(this.s, 1)); + this.v = Math.max(0, Math.min(this.v, 1)); + + if (this.s === 0) { + this.r = this.g = this.b = this.v; + } else { + var var_h = this.h === 1 ? 0 : this.h * 6, + var_i = Math.floor(var_h), + var_1 = this.v * (1 - this.s), + var_2 = this.v * (1 - this.s * (var_h - var_i)), + var_3 = this.v * (1 - this.s * (1 - (var_h - var_i))); + + if (var_i === 0) { + this.r = this.v; + this.g = var_3; + this.b = var_1; + } else if (var_i === 1) { + this.r = var_2; + this.g = this.v; + this.b = var_1; + } else if (var_i === 2) { + this.r = var_1; + this.g = this.v; + this.b = var_3; + } else if (var_i === 3) { + this.r = var_1; + this.g = var_2; + this.b = this.v; + } else if (var_i === 4) { + this.r = var_3; + this.g = var_1; + this.b = this.v; + } else { + this.r = this.v; + this.g = var_1; + this.b = var_2; + } + } + return this; + }; + + this.updateHSV = function () { + var minVal, maxVal, delta, del_R, del_G, del_B; + this.r = Math.max(0, Math.min(this.r, 1)); + this.g = Math.max(0, Math.min(this.g, 1)); + this.b = Math.max(0, Math.min(this.b, 1)); + + minVal = Math.min(this.r, this.g, this.b); + maxVal = Math.max(this.r, this.g, this.b); + delta = maxVal - minVal; + + this.v = maxVal; + + if (delta === 0) { + this.h = 0; + this.s = 0; + } else { + this.s = delta / maxVal; + del_R = (((maxVal - this.r) / 6) + (delta / 2)) / delta; + del_G = (((maxVal - this.g) / 6) + (delta / 2)) / delta; + del_B = (((maxVal - this.b) / 6) + (delta / 2)) / delta; + + if (this.r === maxVal) { + this.h = del_B - del_G; + } else if (this.g === maxVal) { + this.h = (1 / 3) + del_R - del_B; + } else if (this.b === maxVal) { + this.h = (2 / 3) + del_G - del_R; + } + + if (this.h < 0) { + this.h += 1; + } else if (this.h > 1) { + this.h -= 1; + } + } + return this; + }; + + this._hexify = function (number) { + // return Math.round(number).toString(16); + var digits = '0123456789abcdef', + lsd = number % 16, + msd = (number - lsd) / 16, + hexified = digits.charAt(msd) + digits.charAt(lsd); + return hexified; + }; + + this.toHex = function () { + return this._hexify(this.r * 255) + this._hexify(this.g * 255) + this._hexify(this.b * 255); + }; + + this.toCSS = function () { + return '#' + this.toHex(); + }; + + this.toHexAlpha = function () { + return this._hexify(this.a * 255); + }; + + this.copy = function () { + return $.extend({}, this); + }; + + this.normalize = function() { + this.s = 1; + this.v = 1; + this.updateRGB(); + return this; + }; + + this.equals = function (rgb) { + return rgb[0] === this.r + && rgb[1] === this.g + && rgb[2] === this.b; + }; // not really color,move outside! + + this.limit = function (steps) { + steps -= 1; + this.r = Math.round(this.r * steps) / steps; + this.g = Math.round(this.g * steps) / steps; + this.b = Math.round(this.b * steps) / steps; + this.updateHSV(); + }; + + this.set = false; + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 1; + this.h = 0; + this.s = 0; + this.v = 0; + + if (args.length > 0) { + for (arg = 0; arg < args.length; arg += 1) { + args[arg] = Math.max(0, Math.min(args[arg], 1)); + } + + this.set = true; + this.r = args[0] || 0; + this.g = args[1] || 0; + this.b = args[2] || 0; + this.a = args[3] === 0 ? 0 : args[3] || 1; + this.h = args[4] || 0; + this.s = args[5] || 0; + this.v = args[6] || 0; + this.updateHSV(); + } + } + }); + +}(jQuery)); diff --git a/sproutcore/apps/fp/resources/jquery_ui/i18n/jquery.ui.colorpicker-en.js b/sproutcore/apps/fp/resources/jquery_ui/i18n/jquery.ui.colorpicker-en.js new file mode 100644 index 000000000..9c49e2915 --- /dev/null +++ b/sproutcore/apps/fp/resources/jquery_ui/i18n/jquery.ui.colorpicker-en.js @@ -0,0 +1,18 @@ +jQuery(function($) { + $.colorpicker.regional['en'] = { + ok: 'OK', + cancel: 'Cancel', + none: 'None', + revert: 'Color', + button: 'Color', + title: 'Pick a color', + transparent: 'Transparent', + hueShort: 'H', + saturationShort:'S', + valueShort: 'V', + redShort: 'R', + greenShort: 'G', + blueShort: 'B', + alphaShort: 'A' + }; +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/jquery_ui/jquery_ui_core.js b/sproutcore/apps/fp/resources/jquery_ui/jquery_ui_core.js new file mode 100644 index 000000000..6d82ff138 --- /dev/null +++ b/sproutcore/apps/fp/resources/jquery_ui/jquery_ui_core.js @@ -0,0 +1,314 @@ +/*! + * jQuery UI 1.8.17 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function( $, undefined ) { + +// prevent duplicate loading +// this is only a problem because we proxy existing functions +// and we don't want to double proxy them +$.ui = $.ui || {}; +if ( $.ui.version ) { + return; +} + +$.extend( $.ui, { + version: "1.8.17", + + keyCode: { + ALT: 18, + BACKSPACE: 8, + CAPS_LOCK: 20, + COMMA: 188, + COMMAND: 91, + COMMAND_LEFT: 91, // COMMAND + COMMAND_RIGHT: 93, + CONTROL: 17, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + INSERT: 45, + LEFT: 37, + MENU: 93, // COMMAND_RIGHT + NUMPAD_ADD: 107, + NUMPAD_DECIMAL: 110, + NUMPAD_DIVIDE: 111, + NUMPAD_ENTER: 108, + NUMPAD_MULTIPLY: 106, + NUMPAD_SUBTRACT: 109, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SHIFT: 16, + SPACE: 32, + TAB: 9, + UP: 38, + WINDOWS: 91 // COMMAND + } +}); + +// plugins +$.fn.extend({ + propAttr: $.fn.prop || $.fn.attr, + + _focus: $.fn.focus, + focus: function( delay, fn ) { + return typeof delay === "number" ? + this.each(function() { + var elem = this; + setTimeout(function() { + $( elem ).focus(); + if ( fn ) { + fn.call( elem ); + } + }, delay ); + }) : + this._focus.apply( this, arguments ); + }, + + scrollParent: function() { + var scrollParent; + if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) { + scrollParent = this.parents().filter(function() { + return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); + }).eq(0); + } else { + scrollParent = this.parents().filter(function() { + return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1)); + }).eq(0); + } + + return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent; + }, + + zIndex: function( zIndex ) { + if ( zIndex !== undefined ) { + return this.css( "zIndex", zIndex ); + } + + if ( this.length ) { + var elem = $( this[ 0 ] ), position, value; + while ( elem.length && elem[ 0 ] !== document ) { + // Ignore z-index if position is set to a value where z-index is ignored by the browser + // This makes behavior of this function consistent across browsers + // WebKit always returns auto if the element is positioned + position = elem.css( "position" ); + if ( position === "absolute" || position === "relative" || position === "fixed" ) { + // IE returns 0 when zIndex is not specified + // other browsers return a string + // we ignore the case of nested elements with an explicit value of 0 + //
    + value = parseInt( elem.css( "zIndex" ), 10 ); + if ( !isNaN( value ) && value !== 0 ) { + return value; + } + } + elem = elem.parent(); + } + } + + return 0; + }, + + disableSelection: function() { + return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) + + ".ui-disableSelection", function( event ) { + event.preventDefault(); + }); + }, + + enableSelection: function() { + return this.unbind( ".ui-disableSelection" ); + } +}); + +$.each( [ "Width", "Height" ], function( i, name ) { + var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ], + type = name.toLowerCase(), + orig = { + innerWidth: $.fn.innerWidth, + innerHeight: $.fn.innerHeight, + outerWidth: $.fn.outerWidth, + outerHeight: $.fn.outerHeight + }; + + function reduce( elem, size, border, margin ) { + $.each( side, function() { + size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0; + if ( border ) { + size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0; + } + if ( margin ) { + size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0; + } + }); + return size; + } + + $.fn[ "inner" + name ] = function( size ) { + if ( size === undefined ) { + return orig[ "inner" + name ].call( this ); + } + + return this.each(function() { + $( this ).css( type, reduce( this, size ) + "px" ); + }); + }; + + $.fn[ "outer" + name] = function( size, margin ) { + if ( typeof size !== "number" ) { + return orig[ "outer" + name ].call( this, size ); + } + + return this.each(function() { + $( this).css( type, reduce( this, size, true, margin ) + "px" ); + }); + }; +}); + +// selectors +function focusable( element, isTabIndexNotNaN ) { + var nodeName = element.nodeName.toLowerCase(); + if ( "area" === nodeName ) { + var map = element.parentNode, + mapName = map.name, + img; + if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) { + return false; + } + img = $( "img[usemap=#" + mapName + "]" )[0]; + return !!img && visible( img ); + } + return ( /input|select|textarea|button|object/.test( nodeName ) + ? !element.disabled + : "a" == nodeName + ? element.href || isTabIndexNotNaN + : isTabIndexNotNaN) + // the element and all of its ancestors must be visible + && visible( element ); +} + +function visible( element ) { + return !$( element ).parents().andSelf().filter(function() { + return $.curCSS( this, "visibility" ) === "hidden" || + $.expr.filters.hidden( this ); + }).length; +} + +$.extend( $.expr[ ":" ], { + data: function( elem, i, match ) { + return !!$.data( elem, match[ 3 ] ); + }, + + focusable: function( element ) { + return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) ); + }, + + tabbable: function( element ) { + var tabIndex = $.attr( element, "tabindex" ), + isTabIndexNaN = isNaN( tabIndex ); + return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN ); + } +}); + +// support +$(function() { + var body = document.body, + div = body.appendChild( div = document.createElement( "div" ) ); + + $.extend( div.style, { + minHeight: "100px", + height: "auto", + padding: 0, + borderWidth: 0 + }); + + $.support.minHeight = div.offsetHeight === 100; + $.support.selectstart = "onselectstart" in div; + + // set display to none to avoid a layout bug in IE + // http://dev.jquery.com/ticket/4014 + body.removeChild( div ).style.display = "none"; +}); + + + + + +// deprecated +$.extend( $.ui, { + // $.ui.plugin is deprecated. Use the proxy pattern instead. + plugin: { + add: function( module, option, set ) { + var proto = $.ui[ module ].prototype; + for ( var i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args ) { + var set = instance.plugins[ name ]; + if ( !set || !instance.element[ 0 ].parentNode ) { + return; + } + + for ( var i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } + }, + + // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains() + contains: function( a, b ) { + return document.compareDocumentPosition ? + a.compareDocumentPosition( b ) & 16 : + a !== b && a.contains( b ); + }, + + // only used by resizable + hasScroll: function( el, a ) { + + //If overflow is hidden, the element might have extra content, but the user wants to hide it + if ( $( el ).css( "overflow" ) === "hidden") { + return false; + } + + var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop", + has = false; + + if ( el[ scroll ] > 0 ) { + return true; + } + + // TODO: determine which cases actually cause this to happen + // if the element doesn't have the scroll set, see if it's possible to + // set the scroll + el[ scroll ] = 1; + has = ( el[ scroll ] > 0 ); + el[ scroll ] = 0; + return has; + }, + + // these are odd functions, fix the API or move into individual plugins + isOverAxis: function( x, reference, size ) { + //Determines when x coordinate is over "b" element axis + return ( x > reference ) && ( x < ( reference + size ) ); + }, + isOver: function( y, x, top, left, height, width ) { + //Determines when x, y coordinates is over "b" element + return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width ); + } +}); + +})( jQuery ); diff --git a/sproutcore/apps/fp/resources/jquery_ui/mouse.js b/sproutcore/apps/fp/resources/jquery_ui/mouse.js new file mode 100644 index 000000000..1621679c8 --- /dev/null +++ b/sproutcore/apps/fp/resources/jquery_ui/mouse.js @@ -0,0 +1,163 @@ +/*! + * jQuery UI Mouse 1.8.17 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Mouse + * + * Depends: + * jquery.ui.widget.js + */ + +(function( $, undefined ) { + +var mouseHandled = false; +$( document ).mouseup( function( e ) { + mouseHandled = false; +}); + +$.widget("ui.mouse", { + options: { + cancel: ':input,option', + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var self = this; + + this.element + .bind('mousedown.'+this.widgetName, function(event) { + return self._mouseDown(event); + }) + .bind('click.'+this.widgetName, function(event) { + if (true === $.data(event.target, self.widgetName + '.preventClickEvent')) { + $.removeData(event.target, self.widgetName + '.preventClickEvent'); + event.stopImmediatePropagation(); + return false; + } + }); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.unbind('.'+this.widgetName); + }, + + _mouseDown: function(event) { + // don't let more than one widget handle mouseStart + if( mouseHandled ) { return }; + + // we may have missed mouseup (out of window) + (this._mouseStarted && this._mouseUp(event)); + + this._mouseDownEvent = event; + + var self = this, + btnIsLeft = (event.which == 1), + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = (typeof this.options.cancel == "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false); + if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if (!this.mouseDelayMet) { + this._mouseDelayTimer = setTimeout(function() { + self.mouseDelayMet = true; + }, this.options.delay); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = (this._mouseStart(event) !== false); + if (!this._mouseStarted) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if (true === $.data(event.target, this.widgetName + '.preventClickEvent')) { + $.removeData(event.target, this.widgetName + '.preventClickEvent'); + } + + // these delegates are required to keep context + this._mouseMoveDelegate = function(event) { + return self._mouseMove(event); + }; + this._mouseUpDelegate = function(event) { + return self._mouseUp(event); + }; + $(document) + .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate) + .bind('mouseup.'+this.widgetName, this._mouseUpDelegate); + + event.preventDefault(); + + mouseHandled = true; + return true; + }, + + _mouseMove: function(event) { + // IE mouseup check - mouseup happened when mouse was out of window + if ($.browser.msie && !(document.documentMode >= 9) && !event.button) { + return this._mouseUp(event); + } + + if (this._mouseStarted) { + this._mouseDrag(event); + return event.preventDefault(); + } + + if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) { + this._mouseStarted = + (this._mouseStart(this._mouseDownEvent, event) !== false); + (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event)); + } + + return !this._mouseStarted; + }, + + _mouseUp: function(event) { + $(document) + .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate) + .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate); + + if (this._mouseStarted) { + this._mouseStarted = false; + + if (event.target == this._mouseDownEvent.target) { + $.data(event.target, this.widgetName + '.preventClickEvent', true); + } + + this._mouseStop(event); + } + + return false; + }, + + _mouseDistanceMet: function(event) { + return (Math.max( + Math.abs(this._mouseDownEvent.pageX - event.pageX), + Math.abs(this._mouseDownEvent.pageY - event.pageY) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function(event) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function(event) {}, + _mouseDrag: function(event) {}, + _mouseStop: function(event) {}, + _mouseCapture: function(event) { return true; } +}); + +})(jQuery); diff --git a/sproutcore/apps/fp/resources/jquery_ui/slider.js b/sproutcore/apps/fp/resources/jquery_ui/slider.js new file mode 100644 index 000000000..956151043 --- /dev/null +++ b/sproutcore/apps/fp/resources/jquery_ui/slider.js @@ -0,0 +1,667 @@ +/* + * jQuery UI Slider 1.8.17 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Slider + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ + +(function( $, undefined ) { + +// number of pages in a slider +// (how many times can you page up/down to go through the whole range) +var numPages = 5; + +$.widget( "ui.slider", $.ui.mouse, { + + widgetEventPrefix: "slide", + + options: { + animate: false, + distance: 0, + max: 100, + min: 0, + orientation: "horizontal", + range: false, + step: 1, + value: 0, + values: null + }, + + _create: function() { + var self = this, + o = this.options, + existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ), + handle = "", + handleCount = ( o.values && o.values.length ) || 1, + handles = []; + + this._keySliding = false; + this._mouseSliding = false; + this._animateOff = true; + this._handleIndex = null; + this._detectOrientation(); + this._mouseInit(); + + this.element + .addClass( "ui-slider" + + " ui-slider-" + this.orientation + + " ui-widget" + + " ui-widget-content" + + " ui-corner-all" + + ( o.disabled ? " ui-slider-disabled ui-disabled" : "" ) ); + + this.range = $([]); + + if ( o.range ) { + if ( o.range === true ) { + if ( !o.values ) { + o.values = [ this._valueMin(), this._valueMin() ]; + } + if ( o.values.length && o.values.length !== 2 ) { + o.values = [ o.values[0], o.values[0] ]; + } + } + + this.range = $( "
    " ) + .appendTo( this.element ) + .addClass( "ui-slider-range" + + // note: this isn't the most fittingly semantic framework class for this element, + // but worked best visually with a variety of themes + " ui-widget-header" + + ( ( o.range === "min" || o.range === "max" ) ? " ui-slider-range-" + o.range : "" ) ); + } + + for ( var i = existingHandles.length; i < handleCount; i += 1 ) { + handles.push( handle ); + } + + this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( self.element ) ); + + this.handle = this.handles.eq( 0 ); + + this.handles.add( this.range ).filter( "a" ) + .click(function( event ) { + event.preventDefault(); + }) + .hover(function() { + if ( !o.disabled ) { + $( this ).addClass( "ui-state-hover" ); + } + }, function() { + $( this ).removeClass( "ui-state-hover" ); + }) + .focus(function() { + if ( !o.disabled ) { + $( ".ui-slider .ui-state-focus" ).removeClass( "ui-state-focus" ); + $( this ).addClass( "ui-state-focus" ); + } else { + $( this ).blur(); + } + }) + .blur(function() { + $( this ).removeClass( "ui-state-focus" ); + }); + + this.handles.each(function( i ) { + $( this ).data( "index.ui-slider-handle", i ); + }); + + this.handles + .keydown(function( event ) { + var ret = true, + index = $( this ).data( "index.ui-slider-handle" ), + allowed, + curVal, + newVal, + step; + + if ( self.options.disabled ) { + return; + } + + switch ( event.keyCode ) { + case $.ui.keyCode.HOME: + case $.ui.keyCode.END: + case $.ui.keyCode.PAGE_UP: + case $.ui.keyCode.PAGE_DOWN: + case $.ui.keyCode.UP: + case $.ui.keyCode.RIGHT: + case $.ui.keyCode.DOWN: + case $.ui.keyCode.LEFT: + ret = false; + if ( !self._keySliding ) { + self._keySliding = true; + $( this ).addClass( "ui-state-active" ); + allowed = self._start( event, index ); + if ( allowed === false ) { + return; + } + } + break; + } + + step = self.options.step; + if ( self.options.values && self.options.values.length ) { + curVal = newVal = self.values( index ); + } else { + curVal = newVal = self.value(); + } + + switch ( event.keyCode ) { + case $.ui.keyCode.HOME: + newVal = self._valueMin(); + break; + case $.ui.keyCode.END: + newVal = self._valueMax(); + break; + case $.ui.keyCode.PAGE_UP: + newVal = self._trimAlignValue( curVal + ( (self._valueMax() - self._valueMin()) / numPages ) ); + break; + case $.ui.keyCode.PAGE_DOWN: + newVal = self._trimAlignValue( curVal - ( (self._valueMax() - self._valueMin()) / numPages ) ); + break; + case $.ui.keyCode.UP: + case $.ui.keyCode.RIGHT: + if ( curVal === self._valueMax() ) { + return; + } + newVal = self._trimAlignValue( curVal + step ); + break; + case $.ui.keyCode.DOWN: + case $.ui.keyCode.LEFT: + if ( curVal === self._valueMin() ) { + return; + } + newVal = self._trimAlignValue( curVal - step ); + break; + } + + self._slide( event, index, newVal ); + + return ret; + + }) + .keyup(function( event ) { + var index = $( this ).data( "index.ui-slider-handle" ); + + if ( self._keySliding ) { + self._keySliding = false; + self._stop( event, index ); + self._change( event, index ); + $( this ).removeClass( "ui-state-active" ); + } + + }); + + this._refreshValue(); + + this._animateOff = false; + }, + + destroy: function() { + this.handles.remove(); + this.range.remove(); + + this.element + .removeClass( "ui-slider" + + " ui-slider-horizontal" + + " ui-slider-vertical" + + " ui-slider-disabled" + + " ui-widget" + + " ui-widget-content" + + " ui-corner-all" ) + .removeData( "slider" ) + .unbind( ".slider" ); + + this._mouseDestroy(); + + return this; + }, + + _mouseCapture: function( event ) { + var o = this.options, + position, + normValue, + distance, + closestHandle, + self, + index, + allowed, + offset, + mouseOverHandle; + + if ( o.disabled ) { + return false; + } + + this.elementSize = { + width: this.element.outerWidth(), + height: this.element.outerHeight() + }; + this.elementOffset = this.element.offset(); + + position = { x: event.pageX, y: event.pageY }; + normValue = this._normValueFromMouse( position ); + distance = this._valueMax() - this._valueMin() + 1; + self = this; + this.handles.each(function( i ) { + var thisDistance = Math.abs( normValue - self.values(i) ); + if ( distance > thisDistance ) { + distance = thisDistance; + closestHandle = $( this ); + index = i; + } + }); + + // workaround for bug #3736 (if both handles of a range are at 0, + // the first is always used as the one with least distance, + // and moving it is obviously prevented by preventing negative ranges) + if( o.range === true && this.values(1) === o.min ) { + index += 1; + closestHandle = $( this.handles[index] ); + } + + allowed = this._start( event, index ); + if ( allowed === false ) { + return false; + } + this._mouseSliding = true; + + self._handleIndex = index; + + closestHandle + .addClass( "ui-state-active" ) + .focus(); + + offset = closestHandle.offset(); + mouseOverHandle = !$( event.target ).parents().andSelf().is( ".ui-slider-handle" ); + this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : { + left: event.pageX - offset.left - ( closestHandle.width() / 2 ), + top: event.pageY - offset.top - + ( closestHandle.height() / 2 ) - + ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) - + ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) + + ( parseInt( closestHandle.css("marginTop"), 10 ) || 0) + }; + + if ( !this.handles.hasClass( "ui-state-hover" ) ) { + this._slide( event, index, normValue ); + } + this._animateOff = true; + return true; + }, + + _mouseStart: function( event ) { + return true; + }, + + _mouseDrag: function( event ) { + var position = { x: event.pageX, y: event.pageY }, + normValue = this._normValueFromMouse( position ); + + this._slide( event, this._handleIndex, normValue ); + + return false; + }, + + _mouseStop: function( event ) { + this.handles.removeClass( "ui-state-active" ); + this._mouseSliding = false; + + this._stop( event, this._handleIndex ); + this._change( event, this._handleIndex ); + + this._handleIndex = null; + this._clickOffset = null; + this._animateOff = false; + + return false; + }, + + _detectOrientation: function() { + this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal"; + }, + + _normValueFromMouse: function( position ) { + var pixelTotal, + pixelMouse, + percentMouse, + valueTotal, + valueMouse; + + if ( this.orientation === "horizontal" ) { + pixelTotal = this.elementSize.width; + pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 ); + } else { + pixelTotal = this.elementSize.height; + pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 ); + } + + percentMouse = ( pixelMouse / pixelTotal ); + if ( percentMouse > 1 ) { + percentMouse = 1; + } + if ( percentMouse < 0 ) { + percentMouse = 0; + } + if ( this.orientation === "vertical" ) { + percentMouse = 1 - percentMouse; + } + + valueTotal = this._valueMax() - this._valueMin(); + valueMouse = this._valueMin() + percentMouse * valueTotal; + + return this._trimAlignValue( valueMouse ); + }, + + _start: function( event, index ) { + var uiHash = { + handle: this.handles[ index ], + value: this.value() + }; + if ( this.options.values && this.options.values.length ) { + uiHash.value = this.values( index ); + uiHash.values = this.values(); + } + return this._trigger( "start", event, uiHash ); + }, + + _slide: function( event, index, newVal ) { + var otherVal, + newValues, + allowed; + + if ( this.options.values && this.options.values.length ) { + otherVal = this.values( index ? 0 : 1 ); + + if ( ( this.options.values.length === 2 && this.options.range === true ) && + ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) ) + ) { + newVal = otherVal; + } + + if ( newVal !== this.values( index ) ) { + newValues = this.values(); + newValues[ index ] = newVal; + // A slide can be canceled by returning false from the slide callback + allowed = this._trigger( "slide", event, { + handle: this.handles[ index ], + value: newVal, + values: newValues + } ); + otherVal = this.values( index ? 0 : 1 ); + if ( allowed !== false ) { + this.values( index, newVal, true ); + } + } + } else { + if ( newVal !== this.value() ) { + // A slide can be canceled by returning false from the slide callback + allowed = this._trigger( "slide", event, { + handle: this.handles[ index ], + value: newVal + } ); + if ( allowed !== false ) { + this.value( newVal ); + } + } + } + }, + + _stop: function( event, index ) { + var uiHash = { + handle: this.handles[ index ], + value: this.value() + }; + if ( this.options.values && this.options.values.length ) { + uiHash.value = this.values( index ); + uiHash.values = this.values(); + } + + this._trigger( "stop", event, uiHash ); + }, + + _change: function( event, index ) { + if ( !this._keySliding && !this._mouseSliding ) { + var uiHash = { + handle: this.handles[ index ], + value: this.value() + }; + if ( this.options.values && this.options.values.length ) { + uiHash.value = this.values( index ); + uiHash.values = this.values(); + } + + this._trigger( "change", event, uiHash ); + } + }, + + value: function( newValue ) { + if ( arguments.length ) { + this.options.value = this._trimAlignValue( newValue ); + this._refreshValue(); + this._change( null, 0 ); + return; + } + + return this._value(); + }, + + values: function( index, newValue ) { + var vals, + newValues, + i; + + if ( arguments.length > 1 ) { + this.options.values[ index ] = this._trimAlignValue( newValue ); + this._refreshValue(); + this._change( null, index ); + return; + } + + if ( arguments.length ) { + if ( $.isArray( arguments[ 0 ] ) ) { + vals = this.options.values; + newValues = arguments[ 0 ]; + for ( i = 0; i < vals.length; i += 1 ) { + vals[ i ] = this._trimAlignValue( newValues[ i ] ); + this._change( null, i ); + } + this._refreshValue(); + } else { + if ( this.options.values && this.options.values.length ) { + return this._values( index ); + } else { + return this.value(); + } + } + } else { + return this._values(); + } + }, + + _setOption: function( key, value ) { + var i, + valsLength = 0; + + if ( $.isArray( this.options.values ) ) { + valsLength = this.options.values.length; + } + + $.Widget.prototype._setOption.apply( this, arguments ); + + switch ( key ) { + case "disabled": + if ( value ) { + this.handles.filter( ".ui-state-focus" ).blur(); + this.handles.removeClass( "ui-state-hover" ); + this.handles.propAttr( "disabled", true ); + this.element.addClass( "ui-disabled" ); + } else { + this.handles.propAttr( "disabled", false ); + this.element.removeClass( "ui-disabled" ); + } + break; + case "orientation": + this._detectOrientation(); + this.element + .removeClass( "ui-slider-horizontal ui-slider-vertical" ) + .addClass( "ui-slider-" + this.orientation ); + this._refreshValue(); + break; + case "value": + this._animateOff = true; + this._refreshValue(); + this._change( null, 0 ); + this._animateOff = false; + break; + case "values": + this._animateOff = true; + this._refreshValue(); + for ( i = 0; i < valsLength; i += 1 ) { + this._change( null, i ); + } + this._animateOff = false; + break; + } + }, + + //internal value getter + // _value() returns value trimmed by min and max, aligned by step + _value: function() { + var val = this.options.value; + val = this._trimAlignValue( val ); + + return val; + }, + + //internal values getter + // _values() returns array of values trimmed by min and max, aligned by step + // _values( index ) returns single value trimmed by min and max, aligned by step + _values: function( index ) { + var val, + vals, + i; + + if ( arguments.length ) { + val = this.options.values[ index ]; + val = this._trimAlignValue( val ); + + return val; + } else { + // .slice() creates a copy of the array + // this copy gets trimmed by min and max and then returned + vals = this.options.values.slice(); + for ( i = 0; i < vals.length; i+= 1) { + vals[ i ] = this._trimAlignValue( vals[ i ] ); + } + + return vals; + } + }, + + // returns the step-aligned value that val is closest to, between (inclusive) min and max + _trimAlignValue: function( val ) { + if ( val <= this._valueMin() ) { + return this._valueMin(); + } + if ( val >= this._valueMax() ) { + return this._valueMax(); + } + var step = ( this.options.step > 0 ) ? this.options.step : 1, + valModStep = (val - this._valueMin()) % step, + alignValue = val - valModStep; + + if ( Math.abs(valModStep) * 2 >= step ) { + alignValue += ( valModStep > 0 ) ? step : ( -step ); + } + + // Since JavaScript has problems with large floats, round + // the final value to 5 digits after the decimal point (see #4124) + return parseFloat( alignValue.toFixed(5) ); + }, + + _valueMin: function() { + return this.options.min; + }, + + _valueMax: function() { + return this.options.max; + }, + + _refreshValue: function() { + var oRange = this.options.range, + o = this.options, + self = this, + animate = ( !this._animateOff ) ? o.animate : false, + valPercent, + _set = {}, + lastValPercent, + value, + valueMin, + valueMax; + + if ( this.options.values && this.options.values.length ) { + this.handles.each(function( i, j ) { + valPercent = ( self.values(i) - self._valueMin() ) / ( self._valueMax() - self._valueMin() ) * 100; + _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; + $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); + if ( self.options.range === true ) { + if ( self.orientation === "horizontal" ) { + if ( i === 0 ) { + self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate ); + } + if ( i === 1 ) { + self.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + } else { + if ( i === 0 ) { + self.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate ); + } + if ( i === 1 ) { + self.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + } + } + lastValPercent = valPercent; + }); + } else { + value = this.value(); + valueMin = this._valueMin(); + valueMax = this._valueMax(); + valPercent = ( valueMax !== valueMin ) ? + ( value - valueMin ) / ( valueMax - valueMin ) * 100 : + 0; + _set[ self.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%"; + this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate ); + + if ( oRange === "min" && this.orientation === "horizontal" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate ); + } + if ( oRange === "max" && this.orientation === "horizontal" ) { + this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + if ( oRange === "min" && this.orientation === "vertical" ) { + this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate ); + } + if ( oRange === "max" && this.orientation === "vertical" ) { + this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } ); + } + } + } + +}); + +$.extend( $.ui.slider, { + version: "1.8.17" +}); + +}(jQuery)); diff --git a/sproutcore/apps/fp/resources/jquery_ui/styleEditor.js b/sproutcore/apps/fp/resources/jquery_ui/styleEditor.js new file mode 100644 index 000000000..81cc2932a --- /dev/null +++ b/sproutcore/apps/fp/resources/jquery_ui/styleEditor.js @@ -0,0 +1,495 @@ +/*! * jQuery UI StyleEditor + * + * Andy Likuski + * + * Depends: + * jquery.ui.core.js + * jquery.ui.widget.js + * jquery.ui.mouse.js + * jquery.ui.colorpicker.js + */ + +(function( $, undefined ) { + +var uiStyleEditorClasses = + 'ui-styleEditor ' + + 'ui-widget ' + + 'ui-widget-content ' + + 'ui-corner-all '; + +$.widget("ui.styleEditor", { + options: { + /* Sample data + data: { + name:'Feature Name', + intervalCount:5, + // The colorSequence can be any length. Intermediate values are calculated by d3 to match the intervalCount + colors: ['#FF0000','#00FF00'], + // The values defining the intervals. There should be one more break than intervalCount + breaks: [0, 0.2, 0.4, 0.6, 0.8, 1] + }, + */ + minNumberOfIntervals:4, // The maximum number of data intervals (= number of colors and number breaks - 1) + maxNumberOfIntervals:10, // The maximum number of data intervals (= number of colors and number breaks - 1) + showValues:true, // Set false to only see the colors, not the value range inputs + styleEditorClass: '', + zIndex: 10000, + buttons: null, // pass in button ids. These will become in the buttons pane + buttonsInit: null // pass in a function to init the buttons by #id or .ui-button-[id]. A single argument buttonsPane is a jQuery selector matching the buttonsPane of this widget + }, + // The slider is bi-directionally bound with this value + currentIntervalCount: null, + $undoredo: null, + + /** + * Adjusts all data to the given count. If count is null all data is adjusted to the original count + * @param [count] + */ + setIntervalCount: function(count) { + this.currentIntervalCount = count || this.options.data.intervalCount; + // Update the slider value if it doesn't match + this._updateSlider(); + // Update the fields to match the count + this._update(); + }, + /** + * Adjusts all data to the given object + * @param data: {colorFields:[], valueFields:[]}, where valueFields is optional + * @param [restore]: optional true if this an undo/redo operation + */ + setData: function(data, restore) { + this.currentIntervalCount = data.colorFields.length; + // Update the slider value if it doesn't match + this._updateSlider(); + // Update the fields to match the data + this._update(data, restore); + }, + /*** + * Clear the changes made by the user since loading or the last update + */ + clearChanges: function() { + // Clear the input fields in order to recalculate them + $.each(this._visibleFields(), function(i, fieldName) { + this._removeControlsOfPane(fieldName); + }); + // Reset the interval count to the original value and calculate the data accordingly + this.setIntervalCount(); + }, + /** + * Specifies the field input controls to show + * @return {Array} + * @private + */ + _visibleFields:function() { + return this.options.showValues ? + ['colorFields', 'valueFields'] : + ['colorFields']; + }, + /** + * Change is called after any change to the data fields or number of fields. It adds the data to the undo buffer and triggers the 'change' event. + * @param restore: true if this change is the result of an undo/redo operation + */ + _change: function(restore) { + if (!restore) { + this.$undoredo.undoredo('add', this.currentData()); + } + this._trigger("change", null, {}); + }, + currentValues: function() { + return this._currentValuesOfFields('valueFields'); + }, + currentColors: function() { + return this._currentValuesOfFields('colorFields') + }, + currentData: function() { + var self = this; + return $.mapToDictionary(this._visibleFields(), function(field) { + return [field,self._currentValuesOfFields(field)]; + }); + }, + updateValues: function(values) { + this._update({valueFields:values}); + }, + updateColors: function(colors) { + this._update({colorFields:colors}); + }, + /** + * Takes the first and last value and quantize the intermediate ones between them + * @param values + */ + quantizeValues: function(values) { + this.updateValues($.extremes(this.currentValues())); + }, + /** + * + * Takes the first and last colors and quantize the intermediate ones between them + * @param colors + */ + quantizeColors: function(colors) { + this.updateColors($.extremes(this.currentColors())); + }, + + _create: function() { + var self = this, + options = self.options, + data = options.data; + + // The top level div of the plugin, which is appended to the passed in element + (self.uiStyleEditor = $('
    ')) + .addClass(uiStyleEditorClasses + options.styleEditorClass) + .css({ + 'z-index': options.zIndex + }) + .attr({ + role: 'styleEditor' + }) + .appendTo(self.element); + + // Create the controls based on the initially supplied data + self._createControls(data); + }, + + _init: function() { + }, + + widget: function() { + return this.uiStyleEditor; + }, + + _currentValuesOfFields: function(fieldName) { + return this._currentValues(this._uiPanes[fieldName].set) + }, + _currentValues: function(set) { + var selection = d3.selectAll(set).selectAll('.ui-styleEditor-Input'); + return selection.empty() ? [] : selection[0].map(function(d) {return d.value;}) + }, + _removeControlsOfPane: function(paneName) { + set = this._uiPanes[paneName].set; + var selection = d3.selectAll(set).selectAll('.ui-styleEditor-Item').remove(); + }, + _updateSlider: function() { + if (this._slider.slider("option", "value") != this.currentIntervalCount) { + this._slider.slider( "option", "value", this.currentIntervalCount); + } + }, + + _createControls: function(data) { + var self = this, + // The panes of the control are a set of editable color inputs. + // optional corresponding data values are the ranges of data represented by each color. + // Thus there is always one fewer color than value + panes = [].concat(this._visibleFields()).concat(['slider', 'buttons']), + inputPanes = this._visibleFields(), + // Create a dive pane and sub div for the rows of color fields, value fields, and for the slider + $uiPanes = (self._uiPanes = $.mapToDictionary( + panes, + function(paneName) { + var paneClassName = $.format('ui-styleEditor-{0}Pane ', paneName), + $pane = $('
    ') + .addClass( + paneClassName + + 'ui-styleEditor-pane ' + + 'ui-widget-content ' + + 'ui-helper-clearfix' + ) + .appendTo(self.uiStyleEditor), + $set = $( "
    " ) + .addClass( $.format("ui-styleEditor-{0}Set", paneName) ) + .addClass("ui-styleEditor-set") + .appendTo( $pane ); + elements = {pane:$pane, set:$set}; + // Remove the panel if it already exists + self.uiStyleEditor.find(paneClassName).remove(); + return [paneName,elements]; + } + )), + fieldPaneToControlCreator = { + colorFields: { + data:data.colors, + // Color count matches the slider + deltaFromCount: 0, + mapIncomingDatum: function(d) { + return d; + }, + mapSequencedDatum: function(d) { + return d; + }, + d3: function(selection) { + selection.attr('type', 'color'); + }, + createControl: function(element, d, i) { + return self._colorPicker(element, d, i) + }, + updateValue: function(element, d, i) { + $(element).colorpicker('setColor', d); + }, + d3Sort: 'descending' + }, + valueFields: { + data:data.breaks, + // Value count is one more than the slider, since they represent interval breaks + deltaFromCount: 1, + mapIncomingDatum: function(d) { + return parseInt(d); + }, + mapSequencedDatum: function(d) { + return Math.round(d); + }, + d3: function(selection) { + selection.attr('type', 'text'); + }, + createControl: function(element, d, i) { + return self._valuePicker(element, d, i) + }, + updateValue: function(element, d, i) { + $(element).attr('value',d); + }, + d3Sort: 'ascending' + } + }; + + var $slider = (this._slider = $uiPanes.slider.set); + $slider.slider({min:this.options.minNumberOfIntervals, max:this.options.maxNumberOfIntervals, + change: function(event, ui) { + // Silently update the currentIntervalCount to prevent a circular updates + if (self.currentIntervalCount != ui.value) + self.setIntervalCount(ui.value); + } + }); + var $buttons = $uiPanes.buttons.pane; + var buttonCreator = function(i, buttonNameOrButtonArray) { + if ($.isArray(buttonNameOrButtonArray)) { + var buttonArray = buttonNameOrButtonArray; + $.each(buttonArray, buttonCreator); + $('
    ').appendTo($buttons) + } + else { + var buttonName = buttonNameOrButtonArray; + // Classify the button as a ui-button + var $button = $('') + .attr('id', buttonName) + .addClass($.format('ui-button-{0}', buttonName)) + .appendTo($buttons); + // Classify undo/redo/clear buttons as ui-undoredo buttons as well + if ($.inArray(buttonName, ['undo','redo','clear']) != -1) { + $button.addClass($.format('ui-undoredo-{0}', buttonName)) + } + } + }; + // Create each configured button + $.each(self.options.buttons || [], buttonCreator); + // Call the initializers of standard buttons + self._createStandardButtons($buttons); + // Call custom initializers + if (self.options.buttonsInit) + self.options.buttonsInit(self, $buttons); + + self.$undoredo = self.uiStyleEditor.undoredo({ + undo: function(event, ui) { + self.setData(ui.data, true); + }, + redo: function(event, ui) { + self.setData(ui.data, true); + } + }); + + // Craft an update method from the create controls + // Use force values to change the colors and or values to a given data set. The number actually used depends on the value of the slider, and only the extremes will be reliably used if the slider increment doesn't match the number of values given. + // forceValues is in the form {colorFields:[], valueFields:[]} where one or both data sets may be present + self._update = function(forceValues, restore) { + + var isRestore = restore || false, + intervalCount = parseInt(self.currentIntervalCount); + + // Map the two edit field panes to the corresponding sequence + $.each(inputPanes, function(i,paneName) { + paneConfig = fieldPaneToControlCreator[paneName]; + var set = $uiPanes[paneName].set; + // Get the number of inputs to show based on the intervalCount + // This will be on higher for values and identity for colors + var numberOfData = intervalCount + paneConfig.deltaFromCount; + // The values we use are in descending priority either the forced values, the current values of the inputs, or the default data + values = forceValues && forceValues[paneName] ? + // forced data or + forceValues[paneName] : + $.map($.arrayOrIfEmpty( + // inputs data or + self._currentValues(set), + // default data + paneConfig.data), + function(d) { + // Apply parsing to the data + return paneConfig.mapIncomingDatum(d); + }); + + // Recalculates values after the user changes the number of values + // Currently only the extreme values are considered. + // Any intermediate values are ignored. + var calculateSequence = d3.scale.linear() + .domain([0,numberOfData-1]) + .range($.extremes(values)); + + var fullSequence = $.map(values.length == numberOfData ? + // use specified values of the sequence + values : + // count changed so recalculate values between the extremes + $.map( Number.range(numberOfData), calculateSequence), + // Map the incoming values, such as rounding based on the set + function(value) { + return paneConfig.mapSequencedDatum(value); + }); + + // Scale the font based on the number of values being shown + var calculateFontSize = d3.scale.linear() + .domain([ + paneConfig.deltaFromCount + self.options.minNumberOfIntervals, + paneConfig.deltaFromCount + self.options.maxNumberOfIntervals]) + .range([1,0.7]); + var itemWidth = Math.floor(set.ancestorNonZeroValue('width') / numberOfData)-5; + var fontSize = $.roundFloat(calculateFontSize(numberOfData), 2); + function sizeControls(inputs) { + inputs + .style("width", $.format("{0}px", itemWidth)) + .style('font-size', $.format("{0}em", fontSize)); + } + + // calculate a linear range, this works for colors and ints + // Use d3 to keep number of text fields in sync with the currentIntervalCount + var controls = d3.selectAll(set) + .selectAll('.ui-styleEditor-Item') + // Bind the data using the index, which causes new values to be added to the end + // and removed values to be taken from the end + .data(fullSequence, function(d, i) {return i}); + controls + .select('input') + .each(function(d,i) { + paneConfig.updateValue(this, d,i); + }) + .call(sizeControls); + + // Give each new interval a control + var divs = controls.enter() + .append('div'); + divs + .classed('ui-styleEditor-Item', true) + .append('input') + .classed('ui-styleEditor-Input', true) + .attr('id', function(d,i) { + return $.format('uiStyleEditor{0}Input{1}', paneName, i); + }) + .classed($.format('ui-styleEditor-{0}Input', paneName), true) + .each(function(d,i) { + paneConfig.createControl(this, d,i); + paneConfig.updateValue(this, d,i); + }) + .call(function(selection) { + paneConfig.d3(selection) + }) + .call(sizeControls); + + controls.exit().remove(); + //controls.sort(d3[paneConfig['d3Sort']]); + }); + // Call change to update the undo buffer and send the change event + self._change(isRestore); + }; + + // Set the interval count and thus set the slider and call self._update() + self.setIntervalCount(); + }, + /** + * Defines functionality for the standard buttons + * @param buttonsPane + */ + _createStandardButtons: function(buttonsPane) { + var self = this; + // Updates the intermediate values to the extremes chosen by the user + buttonsPane.find( ".ui-button-quantizeColors" ).button({ + label: 'Quantize Colors', + icons: { + primary: "ui-icon-arrowrefresh-1-e" + } + }) + .click(function() { + self.quantizeColors(); + }); + // Updates the intermediate values to the extremes chosen by the user + buttonsPane.find( ".ui-button-quantizeValues" ).button({ + label: 'Quantize Values', + icons: { + primary: "ui-icon-arrowrefresh-1-e" + } + }) + .click(function() { + self.quantizeValues(); + }); + }, + _setOptions: function( options ) { + var self = this; + + $.each( options, function( key, value ) { + self._setOption( key, value ); + }); + + }, + + _setOption: function(key, value){ + var self = this, + uiStyleEditor = self.uiStyleEditor; + + switch (key) { + case "data": + self._createControls(value); + break; + case "styleEditorClass": + uiStyleEditor + .removeClass(self.options.styleEditorClass) + .addClass(uiStyleEditorClasses + value); + break; + } + + $.Widget.prototype._setOption.apply(self, arguments); + }, + + /*** + * Creates a jquery.ui.colorpicker out of the given dom element + * @param element + */ + _colorPicker:function(element, d, i) { + var self = this; + $(element).colorpicker({ + colorFormat: 'HEX', + showOn: 'both', + altOnChange: false, + showNoneButton: false, + buttonColorize: true, + buttonImageOnly: true, + limit: 'websafe', + parts: ['header', 'bar', 'hex' , + 'rgb', 'preview'], + regional: 'en', + /* This causes a jquery error + alpha: true, + */ + altProperties: 'background-color,color', + zIndex:10000, + close: function(event, ui) { + self._change(false); + } + }); + + return element; + }, + + _valuePicker:function(element, d, i) { + var self = this; + return $('') + .change(function() { + self._change(false); + }); + } + +}); + +}(jQuery)); + diff --git a/sproutcore/apps/fp/resources/jquery_ui/widget.js b/sproutcore/apps/fp/resources/jquery_ui/widget.js new file mode 100644 index 000000000..facd77b3f --- /dev/null +++ b/sproutcore/apps/fp/resources/jquery_ui/widget.js @@ -0,0 +1,272 @@ +/*! + * jQuery UI Widget 1.8.17 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Widget + */ +(function( $, undefined ) { + +// jQuery 1.4+ +if ( $.cleanData ) { + var _cleanData = $.cleanData; + $.cleanData = function( elems ) { + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + try { + $( elem ).triggerHandler( "remove" ); + // http://bugs.jquery.com/ticket/8235 + } catch( e ) {} + } + _cleanData( elems ); + }; +} else { + var _remove = $.fn.remove; + $.fn.remove = function( selector, keepData ) { + return this.each(function() { + if ( !keepData ) { + if ( !selector || $.filter( selector, [ this ] ).length ) { + $( "*", this ).add( [ this ] ).each(function() { + try { + $( this ).triggerHandler( "remove" ); + // http://bugs.jquery.com/ticket/8235 + } catch( e ) {} + }); + } + } + return _remove.call( $(this), selector, keepData ); + }); + }; +} + +$.widget = function( name, base, prototype ) { + var namespace = name.split( "." )[ 0 ], + fullName; + name = name.split( "." )[ 1 ]; + fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + // create selector for plugin + $.expr[ ":" ][ fullName ] = function( elem ) { + return !!$.data( elem, name ); + }; + + $[ namespace ] = $[ namespace ] || {}; + $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + + var basePrototype = new base(); + // we need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from +// $.each( basePrototype, function( key, val ) { +// if ( $.isPlainObject(val) ) { +// basePrototype[ key ] = $.extend( {}, val ); +// } +// }); + basePrototype.options = $.extend( true, {}, basePrototype.options ); + $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { + namespace: namespace, + widgetName: name, + widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, + widgetBaseClass: fullName + }, prototype ); + + $.widget.bridge( name, $[ namespace ][ name ] ); +}; + +$.widget.bridge = function( name, object ) { + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string", + args = Array.prototype.slice.call( arguments, 1 ), + returnValue = this; + + // allow multiple hashes to be passed on init + options = !isMethodCall && args.length ? + $.extend.apply( null, [ true, options ].concat(args) ) : + options; + + // prevent calls to internal methods + if ( isMethodCall && options.charAt( 0 ) === "_" ) { + return returnValue; + } + + if ( isMethodCall ) { + this.each(function() { + var instance = $.data( this, name ), + methodValue = instance && $.isFunction( instance[options] ) ? + instance[ options ].apply( instance, args ) : + instance; + // TODO: add this back in 1.9 and use $.error() (see #5972) +// if ( !instance ) { +// throw "cannot call methods on " + name + " prior to initialization; " + +// "attempted to call method '" + options + "'"; +// } +// if ( !$.isFunction( instance[options] ) ) { +// throw "no such method '" + options + "' for " + name + " widget instance"; +// } +// var methodValue = instance[ options ].apply( instance, args ); + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue; + return false; + } + }); + } else { + this.each(function() { + var instance = $.data( this, name ); + if ( instance ) { + instance.option( options || {} )._init(); + } else { + $.data( this, name, new object( options, this ) ); + } + }); + } + + return returnValue; + }; +}; + +$.Widget = function( options, element ) { + // allow instantiation without initializing for simple inheritance + if ( arguments.length ) { + this._createWidget( options, element ); + } +}; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + options: { + disabled: false + }, + _createWidget: function( options, element ) { + // $.widget.bridge stores the plugin instance, but we do it anyway + // so that it's stored even before the _create function runs + $.data( element, this.widgetName, this ); + this.element = $( element ); + this.options = $.extend( true, {}, + this.options, + this._getCreateOptions(), + options ); + + var self = this; + this.element.bind( "remove." + this.widgetName, function() { + self.destroy(); + }); + + this._create(); + this._trigger( "create" ); + this._init(); + }, + _getCreateOptions: function() { + return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ]; + }, + _create: function() {}, + _init: function() {}, + + destroy: function() { + this.element + .unbind( "." + this.widgetName ) + .removeData( this.widgetName ); + this.widget() + .unbind( "." + this.widgetName ) + .removeAttr( "aria-disabled" ) + .removeClass( + this.widgetBaseClass + "-disabled " + + "ui-state-disabled" ); + }, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key; + + if ( arguments.length === 0 ) { + // don't return a reference to the internal hash + return $.extend( {}, this.options ); + } + + if (typeof key === "string" ) { + if ( value === undefined ) { + return this.options[ key ]; + } + options = {}; + options[ key ] = value; + } + + this._setOptions( options ); + + return this; + }, + _setOptions: function( options ) { + var self = this; + $.each( options, function( key, value ) { + self._setOption( key, value ); + }); + + return this; + }, + _setOption: function( key, value ) { + this.options[ key ] = value; + + if ( key === "disabled" ) { + this.widget() + [ value ? "addClass" : "removeClass"]( + this.widgetBaseClass + "-disabled" + " " + + "ui-state-disabled" ) + .attr( "aria-disabled", value ); + } + + return this; + }, + + enable: function() { + return this._setOption( "disabled", false ); + }, + disable: function() { + return this._setOption( "disabled", true ); + }, + + _trigger: function( type, event, data ) { + var prop, orig, + callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + // the original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + + return !( $.isFunction(callback) && + callback.call( this.element[0], event, data ) === false || + event.isDefaultPrevented() ); + } +}; + +})( jQuery ); diff --git a/sproutcore/apps/fp/resources/loading.rhtml b/sproutcore/apps/fp/resources/loading.rhtml new file mode 100644 index 000000000..f56b3a8e6 --- /dev/null +++ b/sproutcore/apps/fp/resources/loading.rhtml @@ -0,0 +1,19 @@ +<% content_for :loading do %> +<% # Any HTML in this file will be visible on screen while your page loads + # its application JavaScript. SproutCore applications are optimized for + # caching and startup very fast, so your users will often only see this + # content for a brief moment on their first app load, if at all. +%> + + + +
    +
    + +
    + +
    + + + +<% end %> diff --git a/sproutcore/apps/fp/resources/login_page.js b/sproutcore/apps/fp/resources/login_page.js new file mode 100644 index 000000000..e4fa282bb --- /dev/null +++ b/sproutcore/apps/fp/resources/login_page.js @@ -0,0 +1,72 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +// Modified based on http://broadcastingadam.com/2011/04/sproutcore_login_tutorial/ +// The login page should only show up when the user hasn't logged in before. We'll store a cookie with the username and apiKey afterward +Footprint.loginPage = SC.Page.design({ + mainPane: SC.MainPane.design({ + defaultResponder: 'Footprint.statechart', + childViews: 'loginForm'.w(), + + loginForm: SC.View.design({ + layout: { width: 200, height: 160, centerX: 0, centerY: 0 }, + childViews: 'header userName password loginButton'.w(), + + header: SC.LabelView.design({ + layout: { width: 200, height: 24, top: 0, centerX: 0 }, + controlSize: SC.LARGE_CONTROL_SIZE, + value: 'Login Required', + textAlign: SC.ALIGN_CENTER + }), + + userName: SC.TextFieldView.design({ + layout: { width: 150, height: 30, top: 30, centerX: 0}, + hint: 'Username', + valueBinding: 'Footprint.loginController.username' + }), + + password: SC.TextFieldView.design({ + layout: { width: 150, height: 30, top: 80, centerX: 0 }, + hint: 'Password', + type: 'password', + valueBinding: 'Footprint.loginController.password' + }), + + loginButton: SC.ButtonView.design({ + layout: { width: 100, height: 30, top: 120, centerX: 0 }, + controlSize: SC.HUGE_CONTROL_SIZE, + isEnabledBinding: SC.Binding.and('Footprint.loginController.username', 'Footprint.loginController.password'), + title: 'Login', + action: 'doAuthenticate' + }) + }) + }), + loginBypassPane: SC.MainPane.extend({ + childViews: 'loadingView progressView'.w(), + + loadingView: SC.ImageView.extend({ + classNames:'loading-image'.w(), + useCanvas:NO, + layout: {centerX: 0, centerY:0, width:500, height:500}, + value:sc_static('images/loading.png') + }), + progressView: SC.ProgressView.extend({ + layout: {centerX:.0001, centerY:0, width:.2, height:16, top:0.9}, + valueBinding:SC.Binding.oneWay('Footprint.loadingStatusController.content'), + minimum:0, + maximum:10 + }) + }) +}); + diff --git a/sproutcore/apps/fp/resources/main_page.js b/sproutcore/apps/fp/resources/main_page.js new file mode 100644 index 000000000..2ccb9d376 --- /dev/null +++ b/sproutcore/apps/fp/resources/main_page.js @@ -0,0 +1,7 @@ +sc_require('views/main_pane'); + +Footprint.mainPage = SC.Page.design({ + mainPane: Footprint.MainPane, + loadingPane: Footprint.LoadingPane +}); + diff --git a/sproutcore/apps/fp/resources/polymaps-extensions.js b/sproutcore/apps/fp/resources/polymaps-extensions.js new file mode 100644 index 000000000..defa452b2 --- /dev/null +++ b/sproutcore/apps/fp/resources/polymaps-extensions.js @@ -0,0 +1,307 @@ +if (!org) var org = {}; +if (!org.polymaps) org.polymaps = {}; + +(function(po) { + po.mcmap = function() { + var mcmap = po.map(); + + function mcmapmove() { + if (!mcmap) return; + mcmap.savestate(); + } + + mcmap.loadstate = function (clon, clat, czoom) { + var value = getCookie('mapstate'), + centerlon = clon, + centerlat = clat, + centerzoom = czoom; + if (value) { + var tokens = value.split('|'); + centerlon = parseFloat(tokens[0]); + centerlat = parseFloat(tokens[1]); + centerzoom = parseInt(tokens[2]); + } + mcmap.center({lon: centerlon, lat: centerlat}) + .zoom(centerzoom); + return mcmap; + } + + mcmap.savestate = function () { + setCookie('mapstate', mcmap.center().lon + '|' + + mcmap.center().lat + '|' + + mcmap.zoom()); + } + + mcmap + .on("move", mcmapmove) + .loadstate(0, 0, 3); + + return mcmap; + }; +})(org.polymaps); + + +(function(po) { + po.legend = function(map) { + var legend = {}; + + legend.container = function (elt) { + console.log("legend " + elt); + return legend; + } + + return legend; + }; +})(org.polymaps); + + +(function(po) { + po.switcher = function(m, l, o) { + var self = {}, + map, + layers, + current, + options; + + map = m; + layers = l; + options = o ? o : {}; + if (!options.title) options.title = 'Base Layer'; + + /* switch to layer */ + self.switchto = function (name) { + var l = layers[name]; + if (l.map()) { + l.visible(true); + } + else { + map.add(l); + } + if (current) { + current.visible(false); + } + current = l; + } + + self.container = function (elt) { + // Create Legend manipulating the DOM + var main = elt; + var list = document.createElement('div'); + list.setAttribute('id', 'switcher-list'); + // For each layer, create a + for (name in layers) { + var layerid = layers[name].id(); + var input = document.createElement('input'); + input.setAttribute('id', 'switcher-' + layerid); + input.setAttribute('name', 'switcher-radio'); + input.setAttribute('type', 'radio'); + input.setAttribute('value', name); + if (layers[name].map()) input.setAttribute('checked', ''); + + // Link onChange event on radio + input.onchange = function () { + self.switchto(this.getAttribute('value')); + }; + var label = document.createElement('label'); + label.setAttribute('for', 'switcher-' + layerid); + label.innerHTML = name; + + var item = document.createElement('div'); + item.appendChild(input); + item.appendChild(label); + list.appendChild(item); + } + var title = document.createElement('div'); + title.setAttribute('id', 'switcher-title'); + title.innerHTML = options.title; + main.appendChild(title); + main.appendChild(list); + return self; + } + + return self; + }; +})(org.polymaps); + + + +(function(po) { + po.toggler = function(m, l, o) { + var self = {}, + map, + layers, + inputs, + options; + + map = m; + layers = l; + options = o ? o : {}; + if (!options.title) options.title = 'Vector Layers'; + + /* toggle layer */ + self.toggle = function (name) { + var l = layers[name]; + if (!l.map()) { + map.add(l); + l.visible(true); + } + else { + var visible = l.visible(); + l.visible(!visible); + } + if (l && l.visible()) inputs[name].setAttribute('checked', ''); + } + + self.container = function (elt) { + // Create Legend manipulating the DOM + var main = elt; + var list = document.createElement('div'); + list.setAttribute('id', 'togglelayer-list'); + // For each layer, create a + inputs = $.mapObjectToObject(layers, function(name) { + var layerid = layers[name].id(); + var input = document.createElement('input'); + input.setAttribute('id', 'togglelayer-' + layerid); + input.setAttribute('name', 'togglelayer'); + input.setAttribute('type', 'checkbox'); + input.setAttribute('value', name); + if (layers[name].map() && layers[name].visible()) input.setAttribute('checked', ''); + + // Link onChange event on checkbox + input.onchange = function () { + self.toggle(this.getAttribute('value')); + }; + var label = document.createElement('label'); + label.setAttribute('for', 'togglelayer-' + layerid); + label.innerHTML = name; + + var item = document.createElement('div'); + item.appendChild(input); + item.appendChild(label); + list.appendChild(item); + return [name, input]; + }); + var title = document.createElement('div'); + title.setAttribute('id', 'togglelayer-title'); + title.innerHTML = options.title; + main.appendChild(title); + main.appendChild(list); + return self; + } + + return self; + }; +})(org.polymaps); + + +(function(po) { + po.checkbrowser = function(eltid) { + + function isBrowserCompatible() { + ev = getInternetExplorerVersion(); + cv = getChromeVersion(); + fv = getFirefoxVersion(); + sv = getSafariVersion(); + ov = getOperaVersion(); + return ((ev >= 9) || (cv >= 5) || (sv >= 3) || (fv >= 3) || (ov >= 11)); + } + + if (isBrowserCompatible()) { + hide(eltid); + } + }; +})(org.polymaps); + + +function hide(eltid){ + document.getElementById(eltid).style.display='none'; +} + +/* + * + * Browsers Versions Detection + * + */ +function getInternetExplorerVersion() { + //Returns the version of Internet Explorer or a -1 + //(indicating the use of another browser). + var rv = -1; // Return value assumes failure. + if (navigator.appName == 'Microsoft Internet Explorer') + { + var ua = navigator.userAgent; + var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat( RegExp.$1 ); + } + return rv; +} +function getFirefoxVersion() { + var rv = -1; + var ua = navigator.userAgent; + var re = new RegExp("Firefox\\/([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat( RegExp.$1 ); + return rv; +} +function getChromeVersion() { + var rv = -1; + var ua = navigator.userAgent; + var re = new RegExp("Chrome\\/([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat( RegExp.$1 ); + return rv; +} +function getSafariVersion() { + var rv = -1; + var ua = navigator.userAgent; + var re = new RegExp(".*AppleWebKit.*Version\\/([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat( RegExp.$1 ); + return rv; +} +function getOperaVersion() { + var rv = -1; + var ua = navigator.userAgent; + var re = new RegExp("Opera.*Presto.*Version\\/([0-9]{1,}[\.0-9]{0,})"); + if (re.exec(ua) != null) + rv = parseFloat( RegExp.$1 ); + return rv; +} + +/* + * + * Cookies Handling + * + */ +function getCookie( name ) { + var start = document.cookie.indexOf( name + "=" ); + var len = start + name.length + 1; + if ( ( !start ) && ( name != document.cookie.substring( 0, name.length ) ) ) { + return null; + } + if ( start == -1 ) return null; + var end = document.cookie.indexOf( ';', len ); + if ( end == -1 ) end = document.cookie.length; + return unescape( document.cookie.substring( len, end ) ); +} + +function setCookie( name, value, expires, path, domain, secure ) { + var today = new Date(); + today.setTime( today.getTime() ); + if ( expires ) { + expires = expires * 1000 * 60 * 60 * 24; + } + var expires_date = new Date( today.getTime() + (expires) ); + document.cookie = name+'='+escape( value ) + + ( ( expires ) ? ';expires='+expires_date.toGMTString() : '' ) + //expires.toGMTString() + ( ( path ) ? ';path=' + path : '' ) + + ( ( domain ) ? ';domain=' + domain : '' ) + + ( ( secure ) ? ';secure' : '' ); +} + +function deleteCookie( name, path, domain ) { + if ( getCookie( name ) ) document.cookie = name + '=' + + ( ( path ) ? ';path=' + path : '') + + ( ( domain ) ? ';domain=' + domain : '' ) + + ';expires=Thu, 01-Jan-1970 00:00:01 GMT'; +} diff --git a/sproutcore/apps/fp/resources/polymaps.js b/sproutcore/apps/fp/resources/polymaps.js new file mode 100755 index 000000000..9dc2c70b1 --- /dev/null +++ b/sproutcore/apps/fp/resources/polymaps.js @@ -0,0 +1,2402 @@ +if (!org) var org = {}; +if (!org.polymaps) org.polymaps = {}; +(function(po){ + + po.version = "2.5.0"; // semver.org + + var zero = {x: 0, y: 0}; +po.ns = { + svg: "http://www.w3.org/2000/svg", + xlink: "http://www.w3.org/1999/xlink" +}; + +function ns(name) { + var i = name.indexOf(":"); + return i < 0 ? name : { + space: po.ns[name.substring(0, i)], + local: name.substring(i + 1) + }; +} +po.id = (function() { + var id = 0; + return function() { + return ++id; + }; +})(); +po.svg = function(type) { + return document.createElementNS(po.ns.svg, type); +}; +po.transform = function(a, b, c, d, e, f) { + var transform = {}, + zoomDelta, + zoomFraction, + k; + + if (!arguments.length) { + a = 1; c = 0; e = 0; + b = 0; d = 1; f = 0; + } + + transform.zoomFraction = function(x) { + if (!arguments.length) return zoomFraction; + zoomFraction = x; + zoomDelta = Math.floor(zoomFraction + Math.log(Math.sqrt(a * a + b * b + c * c + d * d)) / Math.LN2); + k = Math.pow(2, -zoomDelta); + return transform; + }; + + transform.apply = function(x) { + var k0 = Math.pow(2, -x.zoom), + k1 = Math.pow(2, x.zoom - zoomDelta); + return { + column: (a * x.column * k0 + c * x.row * k0 + e) * k1, + row: (b * x.column * k0 + d * x.row * k0 + f) * k1, + zoom: x.zoom - zoomDelta + }; + }; + + transform.unapply = function(x) { + var k0 = Math.pow(2, -x.zoom), + k1 = Math.pow(2, x.zoom + zoomDelta); + return { + column: (x.column * k0 * d - x.row * k0 * c - e * d + f * c) / (a * d - b * c) * k1, + row: (x.column * k0 * b - x.row * k0 * a - e * b + f * a) / (c * b - d * a) * k1, + zoom: x.zoom + zoomDelta + }; + }; + + transform.toString = function() { + return "matrix(" + [a * k, b * k, c * k, d * k].join(" ") + " 0 0)"; + }; + + return transform.zoomFraction(0); +}; +po.cache = function(load, unload) { + var cache = {}, + locks = {}, + map = {}, + head = null, + tail = null, + size = 64, + n = 0; + + function remove(tile) { + n--; + if (unload) unload(tile); + delete map[tile.key]; + if (tile.next) tile.next.prev = tile.prev; + else if (tail = tile.prev) tail.next = null; + if (tile.prev) tile.prev.next = tile.next; + else if (head = tile.next) head.prev = null; + } + + function flush() { + for (var tile = tail; n > size; tile = tile.prev) { + if (!tile) break; + if (tile.lock) continue; + remove(tile); + } + } + + cache.peek = function(c) { + return map[[c.zoom, c.column, c.row].join("/")]; + }; + + cache.load = function(c, projection) { + var key = [c.zoom, c.column, c.row].join("/"), + tile = map[key]; + if (tile) { + if (tile.prev) { + tile.prev.next = tile.next; + if (tile.next) tile.next.prev = tile.prev; + else tail = tile.prev; + tile.prev = null; + tile.next = head; + head.prev = tile; + head = tile; + } + tile.lock = 1; + locks[key] = tile; + return tile; + } + tile = { + key: key, + column: c.column, + row: c.row, + zoom: c.zoom, + next: head, + prev: null, + lock: 1 + }; + load.call(null, tile, projection); + locks[key] = map[key] = tile; + if (head) head.prev = tile; + else tail = tile; + head = tile; + n++; + return tile; + }; + + cache.unload = function(key) { + if (!(key in locks)) return false; + var tile = locks[key]; + tile.lock = 0; + delete locks[key]; + if (tile.request && tile.request.abort(false)) remove(tile); + return tile; + }; + + cache.locks = function() { + return locks; + }; + + cache.size = function(x) { + if (!arguments.length) return size; + size = x; + flush(); + return cache; + }; + + cache.flush = function() { + flush(); + return cache; + }; + + cache.clear = function() { + for (var key in map) { + var tile = map[key]; + if (tile.request) tile.request.abort(false); + if (unload) unload(map[key]); + if (tile.lock) { + tile.lock = 0; + tile.element.parentNode.removeChild(tile.element); + } + } + locks = {}; + map = {}; + head = tail = null; + n = 0; + return cache; + }; + + return cache; +}; +po.url = function(template) { + var hosts = [], + repeat = true; + + function format(c) { + var max = c.zoom < 0 ? 1 : 1 << c.zoom, + column = c.column; + if (repeat) { + column = c.column % max; + if (column < 0) column += max; + } else if ((column < 0) || (column >= max)) { + return null; + } + return template.replace(/{(.)}/g, function(s, v) { + switch (v) { + case "S": return hosts[(Math.abs(c.zoom) + c.row + column) % hosts.length]; + case "Z": return c.zoom; + case "X": return column; + case "Y": return c.row; + case "B": { + var nw = po.map.coordinateLocation({row: c.row, column: column, zoom: c.zoom}), + se = po.map.coordinateLocation({row: c.row + 1, column: column + 1, zoom: c.zoom}), + pn = Math.ceil(Math.log(c.zoom) / Math.LN2); + return se.lat.toFixed(pn) + + "," + nw.lon.toFixed(pn) + + "," + nw.lat.toFixed(pn) + + "," + se.lon.toFixed(pn); + } + } + return v; + }); + } + + format.template = function(x) { + if (!arguments.length) return template; + template = x; + return format; + }; + + format.hosts = function(x) { + if (!arguments.length) return hosts; + hosts = x; + return format; + }; + + format.repeat = function(x) { + if (!arguments.length) return repeat; + repeat = x; + return format; + }; + + return format; +}; +po.dispatch = function(that) { + var types = {}; + + that.on = function(type, handler) { + var listeners = types[type] || (types[type] = []); + for (var i = 0; i < listeners.length; i++) { + if (listeners[i].handler == handler) return that; // already registered + } + listeners.push({handler: handler, on: true}); + return that; + }; + + that.off = function(type, handler) { + var listeners = types[type]; + if (listeners) for (var i = 0; i < listeners.length; i++) { + var l = listeners[i]; + if (l.handler == handler) { + l.on = false; + listeners.splice(i, 1); + break; + } + } + return that; + }; + + return function(event) { + var listeners = types[event.type]; + if (!listeners) return; + listeners = listeners.slice(); // defensive copy + for (var i = 0; i < listeners.length; i++) { + var l = listeners[i]; + if (l.on) l.handler.call(that, event); + } + }; +}; +po.queue = (function() { + var queued = [], active = 0, size = 6; + + function process() { + if ((active >= size) || !queued.length) return; + active++; + queued.pop()(); + } + + function dequeue(send) { + for (var i = 0; i < queued.length; i++) { + if (queued[i] == send) { + queued.splice(i, 1); + return true; + } + } + return false; + } + + function request(url, callback, mimeType) { + var req; + + function send() { + req = new XMLHttpRequest(); + if (mimeType) { + req.overrideMimeType(mimeType); + } + req.open("GET", url, true); + req.onreadystatechange = function(e) { + if (req.readyState == 4) { + active--; + if (req.status < 300) callback(req); + process(); + } + }; + req.send(null); + } + + function abort(hard) { + if (dequeue(send)) return true; + if (hard && req) { req.abort(); return true; } + return false; + } + + queued.push(send); + process(); + return {abort: abort}; + } + + function text(url, callback, mimeType) { + return request(url, function(req) { + if (req.responseText) callback(req.responseText); + }, mimeType); + } + + /* + * We the override MIME type here so that you can load local files; some + * browsers don't assign a proper MIME type for local files. + */ + + function json(url, callback) { + return request(url, function(req) { + if (req.responseText) callback(JSON.parse(req.responseText)); + }, "application/json"); + } + + function xml(url, callback) { + return request(url, function(req) { + if (req.responseXML) callback(req.responseXML); + }, "application/xml"); + } + + function image(image, src, callback) { + var img; + + function send() { + img = document.createElement("img"); + img.onerror = function() { + active--; + process(); + }; + img.onload = function() { + active--; + callback(img); + process(); + }; + img.src = src; + image.setAttributeNS(po.ns.xlink, "href", src); + } + + function abort(hard) { + if (dequeue(send)) return true; + if (hard && img) { img.src = "about:"; return true; } // cancels request + return false; + } + + queued.push(send); + process(); + return {abort: abort}; + } + + return {text: text, xml: xml, json: json, image: image}; +})(); +po.map = function() { + var map = {}, + container, + size, + sizeActual = zero, + sizeRadius = zero, // sizeActual / 2 + tileSize = {x: 256, y: 256}, + center = {lat: 37.76487, lon: -122.41948}, + zoom = 12, + zoomFraction = 0, + zoomFactor = 1, // Math.pow(2, zoomFraction) + zoomRange = [1, 18], + angle = 0, + angleCos = 1, // Math.cos(angle) + angleSin = 0, // Math.sin(angle) + angleCosi = 1, // Math.cos(-angle) + angleSini = 0, // Math.sin(-angle) + ymin = -180, // lat2y(centerRange[0].lat) + ymax = 180; // lat2y(centerRange[1].lat) + + var centerRange = [ + {lat: y2lat(ymin), lon: -Infinity}, + {lat: y2lat(ymax), lon: Infinity} + ]; + + map.locationCoordinate = function(l) { + var c = po.map.locationCoordinate(l), + k = Math.pow(2, zoom); + c.column *= k; + c.row *= k; + c.zoom += zoom; + return c; + }; + + map.coordinateLocation = po.map.coordinateLocation; + + map.coordinatePoint = function(tileCenter, c) { + var kc = Math.pow(2, zoom - c.zoom), + kt = Math.pow(2, zoom - tileCenter.zoom), + dx = (c.column * kc - tileCenter.column * kt) * tileSize.x * zoomFactor, + dy = (c.row * kc - tileCenter.row * kt) * tileSize.y * zoomFactor; + return { + x: sizeRadius.x + angleCos * dx - angleSin * dy, + y: sizeRadius.y + angleSin * dx + angleCos * dy + }; + }; + + map.pointCoordinate = function(tileCenter, p) { + var kt = Math.pow(2, zoom - tileCenter.zoom), + dx = (p.x - sizeRadius.x) / zoomFactor, + dy = (p.y - sizeRadius.y) / zoomFactor; + return { + column: tileCenter.column * kt + (angleCosi * dx - angleSini * dy) / tileSize.x, + row: tileCenter.row * kt + (angleSini * dx + angleCosi * dy) / tileSize.y, + zoom: zoom + }; + }; + + map.locationPoint = function(l) { + var k = Math.pow(2, zoom + zoomFraction - 3) / 45, + dx = (l.lon - center.lon) * k * tileSize.x, + dy = (lat2y(center.lat) - lat2y(l.lat)) * k * tileSize.y; + return { + x: sizeRadius.x + angleCos * dx - angleSin * dy, + y: sizeRadius.y + angleSin * dx + angleCos * dy + }; + }; + + map.pointLocation = function(p) { + var k = 45 / Math.pow(2, zoom + zoomFraction - 3), + dx = (p.x - sizeRadius.x) * k, + dy = (p.y - sizeRadius.y) * k; + return { + lon: center.lon + (angleCosi * dx - angleSini * dy) / tileSize.x, + lat: y2lat(lat2y(center.lat) - (angleSini * dx + angleCosi * dy) / tileSize.y) + }; + }; + + function rezoom() { + if (zoomRange) { + if (zoom < zoomRange[0]) zoom = zoomRange[0]; + else if (zoom > zoomRange[1]) zoom = zoomRange[1]; + } + zoomFraction = zoom - (zoom = Math.round(zoom)); + zoomFactor = Math.pow(2, zoomFraction); + } + + function recenter() { + if (!centerRange) return; + var k = 45 / Math.pow(2, zoom + zoomFraction - 3); + + // constrain latitude + var y = Math.max(Math.abs(angleSin * sizeRadius.x + angleCos * sizeRadius.y), + Math.abs(angleSini * sizeRadius.x + angleCosi * sizeRadius.y)), + lat0 = y2lat(ymin - y * k / tileSize.y), + lat1 = y2lat(ymax + y * k / tileSize.y); + center.lat = Math.max(lat0, Math.min(lat1, center.lat)); + + // constrain longitude + var x = Math.max(Math.abs(angleSin * sizeRadius.y + angleCos * sizeRadius.x), + Math.abs(angleSini * sizeRadius.y + angleCosi * sizeRadius.x)), + lon0 = centerRange[0].lon - x * k / tileSize.x, + lon1 = centerRange[1].lon + x * k / tileSize.x; + center.lon = Math.max(lon0, Math.min(lon1, center.lon)); + } + + // a place to capture mouse events if no tiles exist + var rect = po.svg("rect"); + rect.setAttribute("visibility", "hidden"); + rect.setAttribute("pointer-events", "all"); + + map.container = function(x) { + if (!arguments.length) return container; + container = x; + container.setAttribute("class", "map"); + container.appendChild(rect); + return map.resize(); // infer size + }; + + map.focusableParent = function() { + for (var p = container; p; p = p.parentNode) { + if (p.tabIndex >= 0) return p; + } + return window; + }; + + map.mouse = function(e) { + var point = (container.ownerSVGElement || container).createSVGPoint(); + if ((bug44083 < 0) && (window.scrollX || window.scrollY)) { + var svg = document.body.appendChild(po.svg("svg")); + svg.style.position = "absolute"; + svg.style.top = svg.style.left = "0px"; + var ctm = svg.getScreenCTM(); + bug44083 = !(ctm.f || ctm.e); + document.body.removeChild(svg); + } + if (bug44083) { + point.x = e.pageX; + point.y = e.pageY; + } else { + point.x = e.clientX; + point.y = e.clientY; + } + return point.matrixTransform(container.getScreenCTM().inverse()); + }; + + map.size = function(x) { + if (!arguments.length) return sizeActual; + size = x; + return map.resize(); // size tiles + }; + + map.resize = function() { + if (!size) { + rect.setAttribute("width", "100%"); + rect.setAttribute("height", "100%"); + b = rect.getBBox(); + sizeActual = {x: b.width, y: b.height}; + resizer.add(map); + } else { + sizeActual = size; + resizer.remove(map); + } + rect.setAttribute("width", sizeActual.x); + rect.setAttribute("height", sizeActual.y); + sizeRadius = {x: sizeActual.x / 2, y: sizeActual.y / 2}; + recenter(); + map.dispatch({type: "resize"}); + return map; + }; + + map.tileSize = function(x) { + if (!arguments.length) return tileSize; + tileSize = x; + map.dispatch({type: "move"}); + return map; + }; + + map.center = function(x) { + if (!arguments.length) return center; + center = x; + recenter(); + map.dispatch({type: "move"}); + return map; + }; + + map.panBy = function(x) { + var k = 45 / Math.pow(2, zoom + zoomFraction - 3), + dx = x.x * k, + dy = x.y * k; + return map.center({ + lon: center.lon + (angleSini * dy - angleCosi * dx) / tileSize.x, + lat: y2lat(lat2y(center.lat) + (angleSini * dx + angleCosi * dy) / tileSize.y) + }); + }; + + map.centerRange = function(x) { + if (!arguments.length) return centerRange; + centerRange = x; + if (centerRange) { + ymin = centerRange[0].lat > -90 ? lat2y(centerRange[0].lat) : -Infinity; + ymax = centerRange[0].lat < 90 ? lat2y(centerRange[1].lat) : Infinity; + } else { + ymin = -Infinity; + ymax = Infinity; + } + recenter(); + map.dispatch({type: "move"}); + return map; + }; + + map.zoom = function(x) { + if (!arguments.length) return zoom + zoomFraction; + zoom = x; + rezoom(); + return map.center(center); + }; + + map.zoomBy = function(z, x0, l) { + if (arguments.length < 2) return map.zoom(zoom + zoomFraction + z); + + // compute the location of x0 + if (arguments.length < 3) l = map.pointLocation(x0); + + // update the zoom level + zoom = zoom + zoomFraction + z; + rezoom(); + + // compute the new point of the location + var x1 = map.locationPoint(l); + + return map.panBy({x: x0.x - x1.x, y: x0.y - x1.y}); + }; + + map.zoomRange = function(x) { + if (!arguments.length) return zoomRange; + zoomRange = x; + return map.zoom(zoom + zoomFraction); + }; + + map.extent = function(x) { + if (!arguments.length) return [ + map.pointLocation({x: 0, y: sizeActual.y}), + map.pointLocation({x: sizeActual.x, y: 0}) + ]; + + // compute the extent in points, scale factor, and center + var bl = map.locationPoint(x[0]), + tr = map.locationPoint(x[1]), + k = Math.max((tr.x - bl.x) / sizeActual.x, (bl.y - tr.y) / sizeActual.y), + l = map.pointLocation({x: (bl.x + tr.x) / 2, y: (bl.y + tr.y) / 2}); + + // update the zoom level + zoom = zoom + zoomFraction - Math.log(k) / Math.LN2; + rezoom(); + + // set the new center + return map.center(l); + }; + + map.angle = function(x) { + if (!arguments.length) return angle; + angle = x; + angleCos = Math.cos(angle); + angleSin = Math.sin(angle); + angleCosi = Math.cos(-angle); + angleSini = Math.sin(-angle); + recenter(); + map.dispatch({type: "move"}); + return map; + }; + + map.add = function(x) { + x.map(map); + return map; + }; + + map.remove = function(x) { + x.map(null); + return map; + }; + + map.dispatch = po.dispatch(map); + + return map; +}; + +function resizer(e) { + for (var i = 0; i < resizer.maps.length; i++) { + resizer.maps[i].resize(); + } +} + +resizer.maps = []; + +resizer.add = function(map) { + for (var i = 0; i < resizer.maps.length; i++) { + if (resizer.maps[i] == map) return; + } + resizer.maps.push(map); +}; + +resizer.remove = function(map) { + for (var i = 0; i < resizer.maps.length; i++) { + if (resizer.maps[i] == map) { + resizer.maps.splice(i, 1); + return; + } + } +}; + +// Note: assumes single window (no frames, iframes, etc.)! +window.addEventListener("resize", resizer, false); +window.addEventListener("load", resizer, false); + +// See http://wiki.openstreetmap.org/wiki/Mercator + +function y2lat(y) { + return 360 / Math.PI * Math.atan(Math.exp(y * Math.PI / 180)) - 90; +} + +function lat2y(lat) { + return 180 / Math.PI * Math.log(Math.tan(Math.PI / 4 + lat * Math.PI / 360)); +} + +po.map.locationCoordinate = function(l) { + var k = 1 / 360; + return { + column: (l.lon + 180) * k, + row: (180 - lat2y(l.lat)) * k, + zoom: 0 + }; +}; + +po.map.coordinateLocation = function(c) { + var k = 45 / Math.pow(2, c.zoom - 3); + return { + lon: k * c.column - 180, + lat: y2lat(180 - k * c.row) + }; +}; + +// https://bugs.webkit.org/show_bug.cgi?id=44083 +var bug44083 = /WebKit/.test(navigator.userAgent) ? -1 : 0; +po.layer = function(load, unload) { + var layer = {}, + cache = layer.cache = po.cache(load, unload).size(512), + tile = true, + visible = true, + zoom, + id, + map, + container = po.svg("g"), + transform, + levelZoom, + levels = {}; + + container.setAttribute("class", "layer"); + for (var i = -4; i <= -1; i++) levels[i] = container.appendChild(po.svg("g")); + for (var i = 2; i >= 1; i--) levels[i] = container.appendChild(po.svg("g")); + levels[0] = container.appendChild(po.svg("g")); + + function zoomIn(z) { + var end = levels[0].nextSibling; + for (; levelZoom < z; levelZoom++) { + // -4, -3, -2, -1, +2, +1, =0 // current order + // -3, -2, -1, +2, +1, =0, -4 // insertBefore(-4, end) + // -3, -2, -1, +1, =0, -4, +2 // insertBefore(+2, end) + // -3, -2, -1, =0, -4, +2, +1 // insertBefore(+1, end) + // -4, -3, -2, -1, +2, +1, =0 // relabel + container.insertBefore(levels[-4], end); + container.insertBefore(levels[2], end); + container.insertBefore(levels[1], end); + var t = levels[-4]; + for (var dz = -4; dz < 2;) levels[dz] = levels[++dz]; + levels[dz] = t; + } + } + + function zoomOut(z) { + var end = levels[0].nextSibling; + for (; levelZoom > z; levelZoom--) { + // -4, -3, -2, -1, +2, +1, =0 // current order + // -4, -3, -2, +2, +1, =0, -1 // insertBefore(-1, end) + // +2, -4, -3, -2, +1, =0, -1 // insertBefore(+2, -4) + // -4, -3, -2, -1, +2, +1, =0 // relabel + container.insertBefore(levels[-1], end); + container.insertBefore(levels[2], levels[-4]); + var t = levels[2]; + for (var dz = 2; dz > -4;) levels[dz] = levels[--dz]; + levels[dz] = t; + } + } + + function move() { + var map = layer.map(), // in case the layer is removed + mapZoom = map.zoom(), + mapZoomFraction = mapZoom - (mapZoom = Math.round(mapZoom)), + mapSize = map.size(), + mapAngle = map.angle(), + tileSize = map.tileSize(), + tileCenter = map.locationCoordinate(map.center()); + + // set the layer zoom levels + if (levelZoom != mapZoom) { + if (levelZoom < mapZoom) zoomIn(mapZoom); + else if (levelZoom > mapZoom) zoomOut(mapZoom); + else levelZoom = mapZoom; + for (var z = -4; z <= 2; z++) { + var l = levels[z]; + l.setAttribute("class", "zoom" + (z < 0 ? "" : "+") + z + " zoom" + (mapZoom + z)); + l.setAttribute("transform", "scale(" + Math.pow(2, -z) + ")"); + } + } + + // set the layer transform + container.setAttribute("transform", + "translate(" + (mapSize.x / 2) + "," + (mapSize.y / 2) + ")" + + (mapAngle ? "rotate(" + mapAngle / Math.PI * 180 + ")" : "") + + (mapZoomFraction ? "scale(" + Math.pow(2, mapZoomFraction) + ")" : "") + + (transform ? transform.zoomFraction(mapZoomFraction) : "")); + + // get the coordinates of the four corners + var c0 = map.pointCoordinate(tileCenter, zero), + c1 = map.pointCoordinate(tileCenter, {x: mapSize.x, y: 0}), + c2 = map.pointCoordinate(tileCenter, mapSize), + c3 = map.pointCoordinate(tileCenter, {x: 0, y: mapSize.y}); + + // round to pixel boundary to avoid anti-aliasing artifacts + if (!mapZoomFraction && !mapAngle && !transform) { + tileCenter.column = (Math.round(tileSize.x * tileCenter.column) + (mapSize.x & 1) / 2) / tileSize.x; + tileCenter.row = (Math.round(tileSize.y * tileCenter.row) + (mapSize.y & 1) / 2) / tileSize.y; + } + + // layer-specific coordinate transform + if (transform) { + c0 = transform.unapply(c0); + c1 = transform.unapply(c1); + c2 = transform.unapply(c2); + c3 = transform.unapply(c3); + tileCenter = transform.unapply(tileCenter); + } + + // layer-specific zoom transform + var tileLevel = zoom ? zoom(c0.zoom) - c0.zoom : 0; + if (tileLevel) { + var k = Math.pow(2, tileLevel); + c0.column *= k; c0.row *= k; + c1.column *= k; c1.row *= k; + c2.column *= k; c2.row *= k; + c3.column *= k; c3.row *= k; + c0.zoom = c1.zoom = c2.zoom = c3.zoom += tileLevel; + } + + // tile-specific projection + function projection(c) { + var zoom = c.zoom, + max = zoom < 0 ? 1 : 1 << zoom, + column = c.column % max, + row = c.row; + if (column < 0) column += max; + return { + locationPoint: function(l) { + var c = po.map.locationCoordinate(l), + k = Math.pow(2, zoom - c.zoom); + return { + x: tileSize.x * (k * c.column - column), + y: tileSize.y * (k * c.row - row) + }; + } + }; + } + + // record which tiles are visible + var oldLocks = cache.locks(), newLocks = {}; + + // reset the proxy counts + for (var key in oldLocks) { + oldLocks[key].proxyCount = 0; + } + + // load the tiles! + if (visible && tileLevel > -5 && tileLevel < 3) { + var ymax = c0.zoom < 0 ? 1 : 1 << c0.zoom; + if (tile) { + scanTriangle(c0, c1, c2, 0, ymax, scanLine); + scanTriangle(c2, c3, c0, 0, ymax, scanLine); + } else { + var x = Math.floor((c0.column + c2.column) / 2), + y = Math.max(0, Math.min(ymax - 1, Math.floor((c1.row + c3.row) / 2))), + z = Math.min(4, c0.zoom); + x = x >> z << z; + y = y >> z << z; + scanLine(x, x + 1, y); + } + } + + // scan-line conversion + function scanLine(x0, x1, y) { + var z = c0.zoom, + z0 = 2 - tileLevel, + z1 = 4 + tileLevel; + + for (var x = x0; x < x1; x++) { + var t = cache.load({column: x, row: y, zoom: z}, projection); + if (!t.ready && !(t.key in newLocks)) { + t.proxyRefs = {}; + var c, full, proxy; + + // downsample high-resolution tiles + for (var dz = 1; dz <= z0; dz++) { + full = true; + for (var dy = 0, k = 1 << dz; dy <= k; dy++) { + for (var dx = 0; dx <= k; dx++) { + proxy = cache.peek(c = { + column: (x << dz) + dx, + row: (y << dz) + dy, + zoom: z + dz + }); + if (proxy && proxy.ready) { + newLocks[proxy.key] = cache.load(c); + proxy.proxyCount++; + t.proxyRefs[proxy.key] = proxy; + } else { + full = false; + } + } + } + if (full) break; + } + + // upsample low-resolution tiles + if (!full) { + for (var dz = 1; dz <= z1; dz++) { + proxy = cache.peek(c = { + column: x >> dz, + row: y >> dz, + zoom: z - dz + }); + if (proxy && proxy.ready) { + newLocks[proxy.key] = cache.load(c); + proxy.proxyCount++; + t.proxyRefs[proxy.key] = proxy; + break; + } + } + } + } + newLocks[t.key] = t; + } + } + + // position tiles + for (var key in newLocks) { + var t = newLocks[key], + k = Math.pow(2, t.level = t.zoom - tileCenter.zoom); + t.element.setAttribute("transform", "translate(" + + (t.x = tileSize.x * (t.column - tileCenter.column * k)) + "," + + (t.y = tileSize.y * (t.row - tileCenter.row * k)) + ")"); + } + + // remove tiles that are no longer visible + for (var key in oldLocks) { + if (!(key in newLocks)) { + var t = cache.unload(key); + t.element.parentNode.removeChild(t.element); + delete t.proxyRefs; + } + } + + // append tiles that are now visible + for (var key in newLocks) { + var t = newLocks[key]; + if (t.element.parentNode != levels[t.level]) { + levels[t.level].appendChild(t.element); + if (layer.show) layer.show(t); + } + } + + // flush the cache, clearing no-longer-needed tiles + cache.flush(); + + // dispatch the move event + layer.dispatch({type: "move"}); + } + + // remove proxy tiles when tiles load + function cleanup(e) { + if (e.tile.proxyRefs) { + for (var proxyKey in e.tile.proxyRefs) { + var proxyTile = e.tile.proxyRefs[proxyKey]; + if ((--proxyTile.proxyCount <= 0) && cache.unload(proxyKey)) { + proxyTile.element.parentNode.removeChild(proxyTile.element); + } + } + delete e.tile.proxyRefs; + } + } + + layer.map = function(x) { + if (!arguments.length) return map; + if (map) { + if (map == x) { + container.parentNode.appendChild(container); // move to end + return layer; + } + map.off("move", move).off("resize", move); + container.parentNode.removeChild(container); + } + map = x; + if (map) { + map.container().appendChild(container); + if (layer.init) layer.init(container); + map.on("move", move).on("resize", move); + move(); + } + return layer; + }; + + layer.container = function() { + return container; + }; + + layer.levels = function() { + return levels; + }; + + layer.id = function(x) { + if (!arguments.length) return id; + id = x; + container.setAttribute("id", x); + return layer; + }; + + layer.visible = function(x) { + if (!arguments.length) return visible; + if (visible = x) container.removeAttribute("visibility") + else container.setAttribute("visibility", "hidden"); + if (map) move(); + return layer; + }; + + layer.transform = function(x) { + if (!arguments.length) return transform; + transform = x; + if (map) move(); + return layer; + }; + + layer.zoom = function(x) { + if (!arguments.length) return zoom; + zoom = typeof x == "function" || x == null ? x : function() { return x; }; + if (map) move(); + return layer; + }; + + layer.tile = function(x) { + if (!arguments.length) return tile; + tile = x; + if (map) move(); + return layer; + }; + + layer.reload = function() { + cache.clear(); + if (map) move(); + return layer; + }; + + layer.dispatch = po.dispatch(layer); + layer.on("load", cleanup); + + return layer; +}; + +// scan-line conversion +function edge(a, b) { + if (a.row > b.row) { var t = a; a = b; b = t; } + return { + x0: a.column, + y0: a.row, + x1: b.column, + y1: b.row, + dx: b.column - a.column, + dy: b.row - a.row + }; +} + +// scan-line conversion +function scanSpans(e0, e1, ymin, ymax, scanLine) { + var y0 = Math.max(ymin, Math.floor(e1.y0)), + y1 = Math.min(ymax, Math.ceil(e1.y1)); + + // sort edges by x-coordinate + if ((e0.x0 == e1.x0 && e0.y0 == e1.y0) + ? (e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) + : (e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0)) { + var t = e0; e0 = e1; e1 = t; + } + + // scan lines! + var m0 = e0.dx / e0.dy, + m1 = e1.dx / e1.dy, + d0 = e0.dx > 0, // use y + 1 to compute x0 + d1 = e1.dx < 0; // use y + 1 to compute x1 + for (var y = y0; y < y1; y++) { + var x0 = m0 * Math.max(0, Math.min(e0.dy, y + d0 - e0.y0)) + e0.x0, + x1 = m1 * Math.max(0, Math.min(e1.dy, y + d1 - e1.y0)) + e1.x0; + scanLine(Math.floor(x1), Math.ceil(x0), y); + } +} + +// scan-line conversion +function scanTriangle(a, b, c, ymin, ymax, scanLine) { + var ab = edge(a, b), + bc = edge(b, c), + ca = edge(c, a); + + // sort edges by y-length + if (ab.dy > bc.dy) { var t = ab; ab = bc; bc = t; } + if (ab.dy > ca.dy) { var t = ab; ab = ca; ca = t; } + if (bc.dy > ca.dy) { var t = bc; bc = ca; ca = t; } + + // scan span! scan span! + if (ab.dy) scanSpans(ca, ab, ymin, ymax, scanLine); + if (bc.dy) scanSpans(ca, bc, ymin, ymax, scanLine); +} +po.image = function() { + var image = po.layer(load, unload), + url; + + function load(tile) { + var element = tile.element = po.svg("image"), size = image.map().tileSize(); + element.setAttribute("preserveAspectRatio", "none"); + element.setAttribute("width", size.x); + element.setAttribute("height", size.y); + + if (typeof url == "function") { + element.setAttribute("opacity", 0); + var tileUrl = url(tile); + if (tileUrl != null) { + tile.request = po.queue.image(element, tileUrl, function(img) { + delete tile.request; + tile.ready = true; + tile.img = img; + element.removeAttribute("opacity"); + image.dispatch({type: "load", tile: tile}); + }); + } else { + tile.ready = true; + image.dispatch({type: "load", tile: tile}); + } + } else { + tile.ready = true; + if (url != null) element.setAttributeNS(po.ns.xlink, "href", url); + image.dispatch({type: "load", tile: tile}); + } + } + + function unload(tile) { + if (tile.request) tile.request.abort(true); + } + + image.url = function(x) { + if (!arguments.length) return url; + url = typeof x == "string" && /{.}/.test(x) ? po.url(x) : x; + return image.reload(); + }; + + return image; +}; +po.geoJson = function(fetch) { + var geoJson = po.layer(load, unload), + container = geoJson.container(), + url, + clip = true, + clipId = "org.polymaps." + po.id(), + clipHref = "url(#" + clipId + ")", + clipPath = container.insertBefore(po.svg("clipPath"), container.firstChild), + clipRect = clipPath.appendChild(po.svg("rect")), + scale = "auto", + zoom = null, + features; + + container.setAttribute("fill-rule", "evenodd"); + clipPath.setAttribute("id", clipId); + + if (!arguments.length) fetch = po.queue.json; + + function projection(proj) { + var l = {lat: 0, lon: 0}; + return function(coordinates) { + l.lat = coordinates[1]; + l.lon = coordinates[0]; + var p = proj(l); + coordinates.x = p.x; + coordinates.y = p.y; + return p; + }; + } + + function geometry(o, proj) { + return o && o.type in types && types[o.type](o, proj); + } + + var types = { + + Point: function(o, proj) { + var p = proj(o.coordinates), + c = po.svg("circle"); + c.setAttribute("r", 4.5); + c.setAttribute("transform", "translate(" + p.x + "," + p.y + ")"); + return c; + }, + + MultiPoint: function(o, proj) { + var g = po.svg("g"), + c = o.coordinates, + p, // proj(c[i]) + x, // svg:circle + i = -1, + n = c.length; + while (++i < n) { + x = g.appendChild(po.svg("circle")); + x.setAttribute("r", 4.5); + x.setAttribute("transform", "translate(" + (p = proj(c[i])).x + "," + p.y + ")"); + } + return g; + }, + + LineString: function(o, proj) { + var x = po.svg("path"), + d = ["M"], + c = o.coordinates, + p, // proj(c[i]) + i = -1, + n = c.length; + while (++i < n) d.push((p = proj(c[i])).x, ",", p.y, "L"); + d.pop(); + if (!d.length) return; + x.setAttribute("d", d.join("")); + return x; + }, + + MultiLineString: function(o, proj) { + var x = po.svg("path"), + d = [], + ci = o.coordinates, + cj, // ci[i] + i = -1, + j, + n = ci.length, + m; + while (++i < n) { + cj = ci[i]; + j = -1; + m = cj.length; + d.push("M"); + while (++j < m) d.push((p = proj(cj[j])).x, ",", p.y, "L"); + d.pop(); + } + if (!d.length) return; + x.setAttribute("d", d.join("")); + return x; + }, + + Polygon: function(o, proj) { + var x = po.svg("path"), + d = [], + ci = o.coordinates, + cj, // ci[i] + i = -1, + j, + n = ci.length, + m; + while (++i < n) { + cj = ci[i]; + j = -1; + m = cj.length - 1; + d.push("M"); + while (++j < m) d.push((p = proj(cj[j])).x, ",", p.y, "L"); + d[d.length - 1] = "Z"; + } + if (!d.length) return; + x.setAttribute("d", d.join("")); + return x; + }, + + MultiPolygon: function(o, proj) { + var x = po.svg("path"), + d = [], + ci = o.coordinates, + cj, // ci[i] + ck, // cj[j] + i = -1, + j, + k, + n = ci.length, + m, + l; + while (++i < n) { + cj = ci[i]; + j = -1; + m = cj.length; + while (++j < m) { + ck = cj[j]; + k = -1; + l = ck.length - 1; + d.push("M"); + while (++k < l) d.push((p = proj(ck[k])).x, ",", p.y, "L"); + d[d.length - 1] = "Z"; + } + } + if (!d.length) return; + x.setAttribute("d", d.join("")); + return x; + }, + + GeometryCollection: function(o, proj) { + var g = po.svg("g"), + i = -1, + c = o.geometries, + n = c.length, + x; + while (++i < n) { + x = geometry(c[i], proj); + if (x) g.appendChild(x); + } + return g; + } + + }; + + function rescale(o, e, k) { + return o.type in rescales && rescales[o.type](o, e, k); + } + + var rescales = { + + Point: function (o, e, k) { + var p = o.coordinates; + e.setAttribute("transform", "translate(" + p.x + "," + p.y + ")" + k); + }, + + MultiPoint: function (o, e, k) { + var c = o.coordinates, + i = -1, + n = p.length, + x = e.firstChild, + p; + while (++i < n) { + p = c[i]; + x.setAttribute("transform", "translate(" + p.x + "," + p.y + ")" + k); + x = x.nextSibling; + } + } + + }; + + function load(tile, proj) { + var g = tile.element = po.svg("g"); + tile.features = []; + + proj = projection(proj(tile).locationPoint); + + function update(data) { + var updated = []; + + /* Fetch the next batch of features, if so directed. */ + if (data.next) tile.request = fetch(data.next.href, update); + + /* Convert the GeoJSON to SVG. */ + switch (data.type) { + case "FeatureCollection": { + for (var i = 0; i < data.features.length; i++) { + var feature = data.features[i], + element = geometry(feature.geometry, proj); + if (element) updated.push({element: g.appendChild(element), data: feature}); + } + break; + } + case "Feature": { + var element = geometry(data.geometry, proj); + if (element) updated.push({element: g.appendChild(element), data: data}); + break; + } + default: { + var element = geometry(data, proj); + if (element) updated.push({element: g.appendChild(element), data: {type: "Feature", geometry: data}}); + break; + } + } + + tile.ready = true; + updated.push.apply(tile.features, updated); + geoJson.dispatch({type: "load", tile: tile, features: updated}); + } + + if (url != null) { + tile.request = fetch(typeof url == "function" ? url(tile) : url, update); + } else { + update({type: "FeatureCollection", features: features || []}); + } + } + + function unload(tile) { + if (tile.request) tile.request.abort(true); + } + + function move() { + var zoom = geoJson.map().zoom(), + tiles = geoJson.cache.locks(), // visible tiles + key, // key in locks + tile, // locks[key] + features, // tile.features + i, // current feature index + n, // current feature count, features.length + feature, // features[i] + k; // scale transform + if (scale == "fixed") { + for (key in tiles) { + if ((tile = tiles[key]).scale != zoom) { + k = "scale(" + Math.pow(2, tile.zoom - zoom) + ")"; + i = -1; + n = (features = tile.features).length; + while (++i < n) rescale((feature = features[i]).data.geometry, feature.element, k); + tile.scale = zoom; + } + } + } else { + for (key in tiles) { + i = -1; + n = (features = (tile = tiles[key]).features).length; + while (++i < n) rescale((feature = features[i]).data.geometry, feature.element, ""); + delete tile.scale; + } + } + } + + geoJson.url = function(x) { + if (!arguments.length) return url; + url = typeof x == "string" && /{.}/.test(x) ? po.url(x) : x; + if (url != null) features = null; + if (typeof url == "string") geoJson.tile(false); + return geoJson.reload(); + }; + + geoJson.features = function(x) { + if (!arguments.length) return features; + if (features = x) { + url = null; + geoJson.tile(false); + } + return geoJson.reload(); + }; + + geoJson.clip = function(x) { + if (!arguments.length) return clip; + if (clip) container.removeChild(clipPath); + if (clip = x) container.insertBefore(clipPath, container.firstChild); + var locks = geoJson.cache.locks(); + for (var key in locks) { + if (clip) locks[key].element.setAttribute("clip-path", clipHref); + else locks[key].element.removeAttribute("clip-path"); + } + return geoJson; + }; + + var __tile__ = geoJson.tile; + geoJson.tile = function(x) { + if (arguments.length && !x) geoJson.clip(x); + return __tile__.apply(geoJson, arguments); + }; + + var __map__ = geoJson.map; + geoJson.map = function(x) { + if (x && clipRect) { + var size = x.tileSize(); + clipRect.setAttribute("width", size.x); + clipRect.setAttribute("height", size.y); + } + return __map__.apply(geoJson, arguments); + }; + + geoJson.scale = function(x) { + if (!arguments.length) return scale; + if (scale = x) geoJson.on("move", move); + else geoJson.off("move", move); + if (geoJson.map()) move(); + return geoJson; + }; + + geoJson.show = function(tile) { + if (clip) tile.element.setAttribute("clip-path", clipHref); + else tile.element.removeAttribute("clip-path"); + geoJson.dispatch({type: "show", tile: tile, features: tile.features}); + return geoJson; + }; + + geoJson.reshow = function() { + var locks = geoJson.cache.locks(); + for (var key in locks) geoJson.show(locks[key]); + return geoJson; + }; + + return geoJson; +}; +po.dblclick = function() { + var dblclick = {}, + zoom = "mouse", + map, + container; + + function handle(e) { + var z = map.zoom(); + if (e.shiftKey) z = Math.ceil(z) - z - 1; + else z = 1 - z + Math.floor(z); + zoom === "mouse" ? map.zoomBy(z, map.mouse(e)) : map.zoomBy(z); + } + + dblclick.zoom = function(x) { + if (!arguments.length) return zoom; + zoom = x; + return dblclick; + }; + + dblclick.map = function(x) { + if (!arguments.length) return map; + if (map) { + container.removeEventListener("dblclick", handle, false); + container = null; + } + if (map = x) { + container = map.container(); + container.addEventListener("dblclick", handle, false); + } + return dblclick; + }; + + return dblclick; +}; +po.drag = function() { + var drag = {}, + map, + container, + dragging; + + function mousedown(e) { + if (e.shiftKey) return; + dragging = { + x: e.clientX, + y: e.clientY + }; + map.focusableParent().focus(); + e.preventDefault(); + document.body.style.setProperty("cursor", "move", null); + } + + function mousemove(e) { + if (!dragging) return; + map.panBy({x: e.clientX - dragging.x, y: e.clientY - dragging.y}); + dragging.x = e.clientX; + dragging.y = e.clientY; + } + + function mouseup(e) { + if (!dragging) return; + mousemove(e); + dragging = null; + document.body.style.removeProperty("cursor"); + } + + drag.map = function(x) { + if (!arguments.length) return map; + if (map) { + container.removeEventListener("mousedown", mousedown, false); + container = null; + } + if (map = x) { + container = map.container(); + container.addEventListener("mousedown", mousedown, false); + } + return drag; + }; + + window.addEventListener("mousemove", mousemove, false); + window.addEventListener("mouseup", mouseup, false); + + return drag; +}; +po.wheel = function() { + var wheel = {}, + timePrev = 0, + last = 0, + smooth = true, + zoom = "mouse", + location, + map, + container; + + function move(e) { + location = null; + } + + // mousewheel events are totally broken! + // https://bugs.webkit.org/show_bug.cgi?id=40441 + // not only that, but Chrome and Safari differ in re. to acceleration! + var inner = document.createElement("div"), + outer = document.createElement("div"); + outer.style.visibility = "hidden"; + outer.style.top = "0px"; + outer.style.height = "0px"; + outer.style.width = "0px"; + outer.style.overflowY = "scroll"; + inner.style.height = "2000px"; + outer.appendChild(inner); + document.body.appendChild(outer); + + function mousewheel(e) { + var delta = e.wheelDelta || -e.detail, + point; + + /* Detect the pixels that would be scrolled by this wheel event. */ + if (delta) { + if (smooth) { + try { + outer.scrollTop = 1000; + outer.dispatchEvent(e); + delta = 1000 - outer.scrollTop; + } catch (error) { + // Derp! Hope for the best? + } + delta *= .005; + } + + /* If smooth zooming is disabled, batch events into unit steps. */ + else { + var timeNow = Date.now(); + if (timeNow - timePrev > 200) { + delta = delta > 0 ? +1 : -1; + timePrev = timeNow; + } else { + delta = 0; + } + } + } + + if (delta) { + switch (zoom) { + case "mouse": { + point = map.mouse(e); + if (!location) location = map.pointLocation(point); + map.off("move", move).zoomBy(delta, point, location).on("move", move); + break; + } + case "location": { + map.zoomBy(delta, map.locationPoint(location), location); + break; + } + default: { // center + map.zoomBy(delta); + break; + } + } + } + + e.preventDefault(); + return false; // for Firefox + } + + wheel.smooth = function(x) { + if (!arguments.length) return smooth; + smooth = x; + return wheel; + }; + + wheel.zoom = function(x, l) { + if (!arguments.length) return zoom; + zoom = x; + location = l; + if (map) { + if (zoom == "mouse") map.on("move", move); + else map.off("move", move); + } + return wheel; + }; + + wheel.map = function(x) { + if (!arguments.length) return map; + if (map) { + container.removeEventListener("mousemove", move, false); + container.removeEventListener("mousewheel", mousewheel, false); + container.removeEventListener("MozMousePixelScroll", mousewheel, false); + container = null; + map.off("move", move); + } + if (map = x) { + if (zoom == "mouse") map.on("move", move); + container = map.container(); + container.addEventListener("mousemove", move, false); + container.addEventListener("mousewheel", mousewheel, false); + container.addEventListener("MozMousePixelScroll", mousewheel, false); + } + return wheel; + }; + + return wheel; +}; +po.arrow = function() { + var arrow = {}, + key = {left: 0, right: 0, up: 0, down: 0}, + last = 0, + repeatTimer, + repeatDelay = 250, + repeatInterval = 50, + speed = 16, + map, + parent; + + function keydown(e) { + if (e.ctrlKey || e.altKey || e.metaKey) return; + var now = Date.now(), dx = 0, dy = 0; + switch (e.keyCode) { + case 37: { + if (!key.left) { + last = now; + key.left = 1; + if (!key.right) dx = speed; + } + break; + } + case 39: { + if (!key.right) { + last = now; + key.right = 1; + if (!key.left) dx = -speed; + } + break; + } + case 38: { + if (!key.up) { + last = now; + key.up = 1; + if (!key.down) dy = speed; + } + break; + } + case 40: { + if (!key.down) { + last = now; + key.down = 1; + if (!key.up) dy = -speed; + } + break; + } + default: return; + } + if (dx || dy) map.panBy({x: dx, y: dy}); + if (!repeatTimer && (key.left | key.right | key.up | key.down)) { + repeatTimer = setInterval(repeat, repeatInterval); + } + e.preventDefault(); + } + + function keyup(e) { + last = Date.now(); + switch (e.keyCode) { + case 37: key.left = 0; break; + case 39: key.right = 0; break; + case 38: key.up = 0; break; + case 40: key.down = 0; break; + default: return; + } + if (repeatTimer && !(key.left | key.right | key.up | key.down)) { + repeatTimer = clearInterval(repeatTimer); + } + e.preventDefault(); + } + + function keypress(e) { + switch (e.charCode) { + case 45: case 95: map.zoom(Math.ceil(map.zoom()) - 1); break; // - _ + case 43: case 61: map.zoom(Math.floor(map.zoom()) + 1); break; // = + + default: return; + } + e.preventDefault(); + } + + function repeat() { + if (!map) return; + if (Date.now() < last + repeatDelay) return; + var dx = (key.left - key.right) * speed, + dy = (key.up - key.down) * speed; + if (dx || dy) map.panBy({x: dx, y: dy}); + } + + arrow.map = function(x) { + if (!arguments.length) return map; + if (map) { + parent.removeEventListener("keypress", keypress, false); + parent.removeEventListener("keydown", keydown, false); + parent.removeEventListener("keyup", keyup, false); + parent = null; + } + if (map = x) { + parent = map.focusableParent(); + parent.addEventListener("keypress", keypress, false); + parent.addEventListener("keydown", keydown, false); + parent.addEventListener("keyup", keyup, false); + } + return arrow; + }; + + arrow.speed = function(x) { + if (!arguments.length) return speed; + speed = x; + return arrow; + }; + + return arrow; +}; +po.hash = function() { + var hash = {}, + s0, // cached location.hash + lat = 90 - 1e-8, // allowable latitude range + map; + + var parser = function(map, s) { + var args = s.split("/").map(Number); + if (args.length < 3 || args.some(isNaN)) return true; // replace bogus hash + else { + var size = map.size(); + map.zoomBy(args[0] - map.zoom(), + {x: size.x / 2, y: size.y / 2}, + {lat: Math.min(lat, Math.max(-lat, args[1])), lon: args[2]}); + } + }; + + var formatter = function(map) { + var center = map.center(), + zoom = map.zoom(), + precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)); + return "#" + zoom.toFixed(2) + + "/" + center.lat.toFixed(precision) + + "/" + center.lon.toFixed(precision); + }; + + function move() { + var s1 = formatter(map); + if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map! + } + + function hashchange() { + if (location.hash === s0) return; // ignore spurious hashchange events + if (parser(map, (s0 = location.hash).substring(1))) + move(); // replace bogus hash + } + + hash.map = function(x) { + if (!arguments.length) return map; + if (map) { + map.off("move", move); + window.removeEventListener("hashchange", hashchange, false); + } + if (map = x) { + map.on("move", move); + window.addEventListener("hashchange", hashchange, false); + location.hash ? hashchange() : move(); + } + return hash; + }; + + hash.parser = function(x) { + if (!arguments.length) return parser; + parser = x; + return hash; + }; + + hash.formatter = function(x) { + if (!arguments.length) return formatter; + formatter = x; + return hash; + }; + + return hash; +}; +po.touch = function() { + var touch = {}, + map, + container, + rotate = false, + last = 0, + zoom, + angle, + locations = {}; // touch identifier -> location + + window.addEventListener("touchmove", touchmove, false); + + function touchstart(e) { + var i = -1, + n = e.touches.length, + t = Date.now(); + + // doubletap detection + if ((n == 1) && (t - last < 300)) { + var z = map.zoom(); + map.zoomBy(1 - z + Math.floor(z), map.mouse(e.touches[0])); + e.preventDefault(); + } + last = t; + + // store original zoom & touch locations + zoom = map.zoom(); + angle = map.angle(); + while (++i < n) { + t = e.touches[i]; + locations[t.identifier] = map.pointLocation(map.mouse(t)); + } + } + + function touchmove(e) { + switch (e.touches.length) { + case 1: { // single-touch pan + var t0 = e.touches[0]; + map.zoomBy(0, map.mouse(t0), locations[t0.identifier]); + e.preventDefault(); + break; + } + case 2: { // double-touch pan + zoom + rotate + var t0 = e.touches[0], + t1 = e.touches[1], + p0 = map.mouse(t0), + p1 = map.mouse(t1), + p2 = {x: (p0.x + p1.x) / 2, y: (p0.y + p1.y) / 2}, // center point + c0 = po.map.locationCoordinate(locations[t0.identifier]), + c1 = po.map.locationCoordinate(locations[t1.identifier]), + c2 = {row: (c0.row + c1.row) / 2, column: (c0.column + c1.column) / 2, zoom: 0}, + l2 = po.map.coordinateLocation(c2); // center location + map.zoomBy(Math.log(e.scale) / Math.LN2 + zoom - map.zoom(), p2, l2); + if (rotate) map.angle(e.rotation / 180 * Math.PI + angle); + e.preventDefault(); + break; + } + } + } + + touch.rotate = function(x) { + if (!arguments.length) return rotate; + rotate = x; + return touch; + }; + + touch.map = function(x) { + if (!arguments.length) return map; + if (map) { + container.removeEventListener("touchstart", touchstart, false); + container = null; + } + if (map = x) { + container = map.container(); + container.addEventListener("touchstart", touchstart, false); + } + return touch; + }; + + return touch; +}; +// Default map controls. +po.interact = function() { + var interact = {}, + drag = po.drag(), + wheel = po.wheel(), + dblclick = po.dblclick(), + touch = po.touch(), + arrow = po.arrow(); + + interact.map = function(x) { + drag.map(x); + wheel.map(x); + dblclick.map(x); + touch.map(x); + arrow.map(x); + return interact; + }; + + return interact; +}; +po.compass = function() { + var compass = {}, + g = po.svg("g"), + ticks = {}, + r = 30, + speed = 16, + last = 0, + repeatDelay = 250, + repeatInterval = 50, + position = "top-left", // top-left, top-right, bottom-left, bottom-right + zoomStyle = "small", // none, small, big + zoomContainer, + panStyle = "small", // none, small + panTimer, + panDirection, + panContainer, + drag, + dragRect = po.svg("rect"), + map, + container, + window; + + g.setAttribute("class", "compass"); + dragRect.setAttribute("class", "back fore"); + dragRect.setAttribute("pointer-events", "none"); + dragRect.setAttribute("display", "none"); + + function panStart(e) { + g.setAttribute("class", "compass active"); + if (!panTimer) panTimer = setInterval(panRepeat, repeatInterval); + if (panDirection) map.panBy(panDirection); + last = Date.now(); + return cancel(e); + } + + function panRepeat() { + if (panDirection && (Date.now() > last + repeatDelay)) { + map.panBy(panDirection); + } + } + + function mousedown(e) { + if (e.shiftKey) { + drag = {x0: map.mouse(e)}; + map.focusableParent().focus(); + return cancel(e); + } + } + + function mousemove(e) { + if (!drag) return; + drag.x1 = map.mouse(e); + dragRect.setAttribute("x", Math.min(drag.x0.x, drag.x1.x)); + dragRect.setAttribute("y", Math.min(drag.x0.y, drag.x1.y)); + dragRect.setAttribute("width", Math.abs(drag.x0.x - drag.x1.x)); + dragRect.setAttribute("height", Math.abs(drag.x0.y - drag.x1.y)); + dragRect.removeAttribute("display"); + } + + function mouseup(e) { + g.setAttribute("class", "compass"); + if (drag) { + if (drag.x1) { + map.extent([ + map.pointLocation({ + x: Math.min(drag.x0.x, drag.x1.x), + y: Math.max(drag.x0.y, drag.x1.y) + }), + map.pointLocation({ + x: Math.max(drag.x0.x, drag.x1.x), + y: Math.min(drag.x0.y, drag.x1.y) + }) + ]); + dragRect.setAttribute("display", "none"); + } + drag = null; + } + if (panTimer) { + clearInterval(panTimer); + panTimer = 0; + } + } + + function panBy(x) { + return function() { + x ? this.setAttribute("class", "active") : this.removeAttribute("class"); + panDirection = x; + }; + } + + function zoomBy(x) { + return function(e) { + g.setAttribute("class", "compass active"); + var z = map.zoom(); + map.zoom(x < 0 ? Math.ceil(z) - 1 : Math.floor(z) + 1); + return cancel(e); + }; + } + + function zoomTo(x) { + return function(e) { + map.zoom(x); + return cancel(e); + }; + } + + function zoomOver() { + this.setAttribute("class", "active"); + } + + function zoomOut() { + this.removeAttribute("class"); + } + + function cancel(e) { + e.stopPropagation(); + e.preventDefault(); + return false; + } + + function pan(by) { + var x = Math.SQRT1_2 * r, + y = r * .7, + z = r * .2, + g = po.svg("g"), + dir = g.appendChild(po.svg("path")), + chv = g.appendChild(po.svg("path")); + dir.setAttribute("class", "direction"); + dir.setAttribute("pointer-events", "all"); + dir.setAttribute("d", "M0,0L" + x + "," + x + "A" + r + "," + r + " 0 0,1 " + -x + "," + x + "Z"); + chv.setAttribute("class", "chevron"); + chv.setAttribute("d", "M" + z + "," + (y - z) + "L0," + y + " " + -z + "," + (y - z)); + chv.setAttribute("pointer-events", "none"); + g.addEventListener("mousedown", panStart, false); + g.addEventListener("mouseover", panBy(by), false); + g.addEventListener("mouseout", panBy(null), false); + g.addEventListener("dblclick", cancel, false); + return g; + } + + function zoom(by) { + var x = r * .4, + y = x / 2, + g = po.svg("g"), + back = g.appendChild(po.svg("path")), + dire = g.appendChild(po.svg("path")), + chev = g.appendChild(po.svg("path")), + fore = g.appendChild(po.svg("path")); + back.setAttribute("class", "back"); + back.setAttribute("d", "M" + -x + ",0V" + -x + "A" + x + "," + x + " 0 1,1 " + x + "," + -x + "V0Z"); + dire.setAttribute("class", "direction"); + dire.setAttribute("d", back.getAttribute("d")); + chev.setAttribute("class", "chevron"); + chev.setAttribute("d", "M" + -y + "," + -x + "H" + y + (by > 0 ? "M0," + (-x - y) + "V" + -y : "")); + fore.setAttribute("class", "fore"); + fore.setAttribute("fill", "none"); + fore.setAttribute("d", back.getAttribute("d")); + g.addEventListener("mousedown", zoomBy(by), false); + g.addEventListener("mouseover", zoomOver, false); + g.addEventListener("mouseout", zoomOut, false); + g.addEventListener("dblclick", cancel, false); + return g; + } + + function tick(i) { + var x = r * .2, + y = r * .4, + g = po.svg("g"), + back = g.appendChild(po.svg("rect")), + chev = g.appendChild(po.svg("path")); + back.setAttribute("pointer-events", "all"); + back.setAttribute("fill", "none"); + back.setAttribute("x", -y); + back.setAttribute("y", -.75 * y); + back.setAttribute("width", 2 * y); + back.setAttribute("height", 1.5 * y); + chev.setAttribute("class", "chevron"); + chev.setAttribute("d", "M" + -x + ",0H" + x); + g.addEventListener("mousedown", zoomTo(i), false); + g.addEventListener("dblclick", cancel, false); + return g; + } + + function move() { + var x = r + 6, y = x, size = map.size(); + switch (position) { + case "top-left": break; + case "top-right": x = size.x - x; break; + case "bottom-left": y = size.y - y; break; + case "bottom-right": x = size.x - x; y = size.y - y; break; + } + g.setAttribute("transform", "translate(" + x + "," + y + ")"); + dragRect.setAttribute("transform", "translate(" + -x + "," + -y + ")"); + for (var i in ticks) { + i == map.zoom() + ? ticks[i].setAttribute("class", "active") + : ticks[i].removeAttribute("class"); + } + } + + function draw() { + while (g.lastChild) g.removeChild(g.lastChild); + + g.appendChild(dragRect); + + if (panStyle != "none") { + panContainer = g.appendChild(po.svg("g")); + panContainer.setAttribute("class", "pan"); + + var back = panContainer.appendChild(po.svg("circle")); + back.setAttribute("class", "back"); + back.setAttribute("r", r); + + var s = panContainer.appendChild(pan({x: 0, y: -speed})); + s.setAttribute("transform", "rotate(0)"); + + var w = panContainer.appendChild(pan({x: speed, y: 0})); + w.setAttribute("transform", "rotate(90)"); + + var n = panContainer.appendChild(pan({x: 0, y: speed})); + n.setAttribute("transform", "rotate(180)"); + + var e = panContainer.appendChild(pan({x: -speed, y: 0})); + e.setAttribute("transform", "rotate(270)"); + + var fore = panContainer.appendChild(po.svg("circle")); + fore.setAttribute("fill", "none"); + fore.setAttribute("class", "fore"); + fore.setAttribute("r", r); + } else { + panContainer = null; + } + + if (zoomStyle != "none") { + zoomContainer = g.appendChild(po.svg("g")); + zoomContainer.setAttribute("class", "zoom"); + + var j = -.5; + if (zoomStyle == "big") { + ticks = {}; + for (var i = map.zoomRange()[0], j = 0; i <= map.zoomRange()[1]; i++, j++) { + (ticks[i] = zoomContainer.appendChild(tick(i))) + .setAttribute("transform", "translate(0," + (-(j + .75) * r * .4) + ")"); + } + } + + var p = panStyle == "none" ? .4 : 2; + zoomContainer.setAttribute("transform", "translate(0," + r * (/^top-/.test(position) ? (p + (j + .5) * .4) : -p) + ")"); + zoomContainer.appendChild(zoom(+1)).setAttribute("transform", "translate(0," + (-(j + .5) * r * .4) + ")"); + zoomContainer.appendChild(zoom(-1)).setAttribute("transform", "scale(-1)"); + } else { + zoomContainer = null; + } + + move(); + } + + compass.radius = function(x) { + if (!arguments.length) return r; + r = x; + if (map) draw(); + return compass; + }; + + compass.speed = function(x) { + if (!arguments.length) return r; + speed = x; + return compass; + }; + + compass.position = function(x) { + if (!arguments.length) return position; + position = x; + if (map) draw(); + return compass; + }; + + compass.pan = function(x) { + if (!arguments.length) return panStyle; + panStyle = x; + if (map) draw(); + return compass; + }; + + compass.zoom = function(x) { + if (!arguments.length) return zoomStyle; + zoomStyle = x; + if (map) draw(); + return compass; + }; + + compass.map = function(x) { + if (!arguments.length) return map; + if (map) { + container.removeEventListener("mousedown", mousedown, false); + container.removeChild(g); + container = null; + window.removeEventListener("mousemove", mousemove, false); + window.removeEventListener("mouseup", mouseup, false); + window = null; + map.off("move", move).off("resize", move); + } + if (map = x) { + container = map.container(); + container.appendChild(g); + container.addEventListener("mousedown", mousedown, false); + window = container.ownerDocument.defaultView; + window.addEventListener("mousemove", mousemove, false); + window.addEventListener("mouseup", mouseup, false); + map.on("move", move).on("resize", move); + draw(); + } + return compass; + }; + + return compass; +}; +po.grid = function() { + var grid = {}, + map, + g = po.svg("g"); + + g.setAttribute("class", "grid"); + + function move(e) { + var p, + line = g.firstChild, + size = map.size(), + nw = map.pointLocation(zero), + se = map.pointLocation(size), + step = Math.pow(2, 4 - Math.round(map.zoom())); + + // Round to step. + nw.lat = Math.floor(nw.lat / step) * step; + nw.lon = Math.ceil(nw.lon / step) * step; + + // Longitude ticks. + for (var x; (x = map.locationPoint(nw).x) <= size.x; nw.lon += step) { + if (!line) line = g.appendChild(po.svg("line")); + line.setAttribute("x1", x); + line.setAttribute("x2", x); + line.setAttribute("y1", 0); + line.setAttribute("y2", size.y); + line = line.nextSibling; + } + + // Latitude ticks. + for (var y; (y = map.locationPoint(nw).y) <= size.y; nw.lat -= step) { + if (!line) line = g.appendChild(po.svg("line")); + line.setAttribute("y1", y); + line.setAttribute("y2", y); + line.setAttribute("x1", 0); + line.setAttribute("x2", size.x); + line = line.nextSibling; + } + + // Remove extra ticks. + while (line) { + var next = line.nextSibling; + g.removeChild(line); + line = next; + } + } + + grid.map = function(x) { + if (!arguments.length) return map; + if (map) { + g.parentNode.removeChild(g); + map.off("move", move).off("resize", move); + } + if (map = x) { + map.on("move", move).on("resize", move); + map.container().appendChild(g); + map.dispatch({type: "move"}); + } + return grid; + }; + + return grid; +}; +po.stylist = function() { + var attrs = [], + styles = [], + title; + + function stylist(e) { + var ne = e.features.length, + na = attrs.length, + ns = styles.length, + f, // feature + d, // data + o, // element + x, // attr or style or title descriptor + v, // attr or style or title value + i, + j; + for (i = 0; i < ne; ++i) { + if (!(o = (f = e.features[i]).element)) continue; + d = f.data; + for (j = 0; j < na; ++j) { + v = (x = attrs[j]).value; + if (typeof v === "function") v = v.call(null, d); + v == null ? (x.name.local + ? o.removeAttributeNS(x.name.space, x.name.local) + : o.removeAttribute(x.name)) : (x.name.local + ? o.setAttributeNS(x.name.space, x.name.local, v) + : o.setAttribute(x.name, v)); + } + for (j = 0; j < ns; ++j) { + v = (x = styles[j]).value; + if (typeof v === "function") v = v.call(null, d); + v == null + ? o.style.removeProperty(x.name) + : o.style.setProperty(x.name, v, x.priority); + } + if (v = title) { + if (typeof v === "function") v = v.call(null, d); + while (o.lastChild) o.removeChild(o.lastChild); + if (v != null) o.appendChild(po.svg("title")).appendChild(document.createTextNode(v)); + } + } + } + + stylist.attr = function(n, v) { + attrs.push({name: ns(n), value: v}); + return stylist; + }; + + stylist.style = function(n, v, p) { + styles.push({name: n, value: v, priority: arguments.length < 3 ? null : p}); + return stylist; + }; + + stylist.title = function(v) { + title = v; + return stylist; + }; + + return stylist; +}; +})(org.polymaps); diff --git a/sproutcore/apps/fp/resources/stylesheets/built_form/color_picker_stylesheet.css b/sproutcore/apps/fp/resources/stylesheets/built_form/color_picker_stylesheet.css new file mode 100644 index 000000000..287c22751 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/built_form/color_picker_stylesheet.css @@ -0,0 +1,59 @@ +@import "compass/css3"; +@import "compass/css3/user-interface"; + + +$theme.sc-view.color-preview { + @include border-radius(10px); + @include box-shadow(rgba(0,0,0,0.5) 0px 0px 5px inset); + border: 1px solid #505050; +} + +$theme.sc-view.color-sub-preview { + @include border-radius(10px); + @include box-shadow(rgba(0,0,0,0.5) 0px 1px 3px); + border: 2px solid #f0f0f0; + border-top-color: #ffffff; + border-bottom-color: #d1d1d1; + + &.active { + @include box-shadow(none); + opacity: 0.8; + } + + &:hover { + cursor: pointer; + } +} + +$theme.sc-label-view.slider-title { + font-family: monospace; + font-size: 16px; + font-weight: bold; + line-height: 24px; + text-align: right; +} + +$theme.sc-label-view.slider-value { + line-height: 24px; + text-align: right; +} + +$theme.sc-text-field-view.color-text { + font-size: 16px; + input { + text-align: center; + } +} + + +$theme.footprint-editable-building-type-attributes-du-label{ + font-size: 10px; +} + +$theme.footprint-editable-building-type-attributes-emp-label{ + font-size: 10px; +} + +$theme.footprint-editable-building-type-attributes-use-pct-label{ + font-size: 10px; +} \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/stylesheets/built_form/placetypes.css b/sproutcore/apps/fp/resources/stylesheets/built_form/placetypes.css new file mode 100644 index 000000000..68cee4794 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/built_form/placetypes.css @@ -0,0 +1,3 @@ +.placetype-a { width:10px; height:10px; background-color:#f00; } +.placetype-b { width:10px; height:10px; background-color:#0f0; } +.placetype-c { width:10px; height:10px; background-color:#00f; } diff --git a/sproutcore/apps/fp/resources/stylesheets/info_views/jquery.colorpicker.css b/sproutcore/apps/fp/resources/stylesheets/info_views/jquery.colorpicker.css new file mode 100644 index 000000000..f223b84c5 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/info_views/jquery.colorpicker.css @@ -0,0 +1,204 @@ +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +.ui-colorpicker, +.ui-dialog.ui-colorpicker { + display: inline-block; + width: auto; +} + +.ui-colorpicker-inline { + position: static; +} + +.ui-colorpicker-buttonset { + float: left; + margin-left: .4em; +} + +.ui-colorpicker-buttonset .ui-button { + margin: .5em 0 .5em 0; + cursor: pointer; +} + +.ui-colorpicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} + +.ui-colorpicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width:auto; + overflow:visible; +} + +.ui-colorpicker-buttonpane button.ui-colorpicker-current { + float:left; +} + +.ui-colorpicker table { + font-size: 100%; /* Reset browser table font-size */ + margin: 0; +} + +.ui-colorpicker table td { + vertical-align: top; +} + +.ui-colorpicker-padding-left { + padding-left: 10px; +} +.ui-colorpicker-padding-top { + padding-top: 10px; +} + +.ui-colorpicker-border { + border: 1px inset; + display: inline-block; +} + +/* Bar & map */ +#ui-colorpicker-map > *, +#ui-colorpicker-bar > * { + position: absolute; + cursor: crosshair; +} + +#ui-colorpicker-map-pointer, +#ui-colorpicker-bar-pointer { + position: absolute; +} +/* Map */ +#ui-colorpicker-map, +#ui-colorpicker-map > * { + display: block; + width: 256px; + height: 256px; + overflow: hidden; +} + +#ui-colorpicker-map-layer-1, +#ui-colorpicker-map-layer-2 { + @include slice("../../images/colorpicker/map.png") no-repeat; +} + +#ui-colorpicker-map-layer-alpha { + @include slice("../../images/colorpicker/map-opacity.png"); +} + +#ui-colorpicker-map-pointer { + display: inline-block; + width: 15px; + height: 15px; + @include slice("../../images/colorpicker/map-pointer.png") no-repeat; +} + + +/* Bar */ +#ui-colorpicker-bar, +#ui-colorpicker-bar > * { + display: block; + width: 20px; + height: 256px; + overflow: hidden; + background-repeat: repeat-x; +} + +#ui-colorpicker-bar-layer-1, +#ui-colorpicker-bar-layer-2, +#ui-colorpicker-bar-layer-3, +#ui-colorpicker-bar-layer-4 { + @include slice("../../images/colorpicker/bar.png") repeat-x; +} + +#ui-colorpicker-bar-layer-alpha { + @include slice("../../images/colorpicker/bar-opacity.png"); +} + +#ui-colorpicker-bar-layer-alphabar { + @include slice("../../images/colorpicker/bar-alpha.png"); +} + +#ui-colorpicker-bar-pointer { + display: inline-block; + width: 20px; + height: 7px; + @include slice("../../images/colorpicker/bar-pointer.png") no-repeat; +} + +/* Preview */ +#ui-colorpicker-preview { + text-align: center; +} + +#ui-colorpicker-preview-initial { + cursor: pointer; +} + +#ui-colorpicker-preview-initial, +#ui-colorpicker-preview-current { + width: 50px; + height: 20px; + display: inline-block; +} + +#ui-colorpicker-preview-initial-alpha, +#ui-colorpicker-preview-current-alpha { + width: 50px; + height: 20px; + display: inline-block; + @include slice("../../images/colorpicker/preview-opacity.png") repeat; +} + +/* Inputs */ +#ui-colorpicker-inputs label, +#ui-colorpicker-hsv label, +#ui-colorpicker-rgb label, +#ui-colorpicker-a label { + width: 1.5em; + display: inline-block; +} + +.ui-colorpicker-number { + margin: .1em; +} + +/* Hex */ +#ui-colorpicker-hex { + text-align: center; +} + +/* Swatches */ +#ui-colorpicker-swatches { + width: 84px; + height: 256px; + overflow: auto; + background-color: #f8f8f8; +} + +.ui-colorpicker-swatch { + cursor: pointer; + float: left; + width: 11px; + height: 11px; + border-right: 1px solid black; + border-bottom: 1px solid black; +} diff --git a/sproutcore/apps/fp/resources/stylesheets/info_views/jquery.styleEditor.css b/sproutcore/apps/fp/resources/stylesheets/info_views/jquery.styleEditor.css new file mode 100644 index 000000000..44114ccd8 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/info_views/jquery.styleEditor.css @@ -0,0 +1,36 @@ +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +.ui-styleEditor-Item +{ + display:inline-block; +} +.ui-styleEditor-Input +{ + width:45px; + height:20px; + font-size:0.8em; + padding:0; +} +.ui-styleEditor-colorFieldsInput { + display:block; +} +.ui-styleEditor-sliderPane { + width:95% +} +.ui-styleEditor-pane { + padding-top: 2px; + padding-bottom: 2px; +} + diff --git a/sproutcore/apps/fp/resources/stylesheets/item_views/item_views.css b/sproutcore/apps/fp/resources/stylesheets/item_views/item_views.css new file mode 100644 index 000000000..d139a524b --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/item_views/item_views.css @@ -0,0 +1,261 @@ +$theme.footprint.sc-view.sc-label-view.footprint-numberrangeitemview-item { + background-color: #FFFFFF; +} + +$theme.footprint.footprint-editable-model-string-view { + text-align: left; +} + +$theme.footprint.footprint-label-select-view { + .footprint-editable-model-string-view { + text-align: right; + } +} + +$theme.footprint-info-pane { + $theme.footprint-editable-model-string-view.is-editable { + &.is-editable { + background-color: #f0f8ff; + border-color: #c3c3c3; + border-width: 2px; + } + } +} + +$theme.footprint.sc-view.sc-pane.sc-panel.sc-palette.footprint-info-pane.opaque.footprint-feature-info-view.panel { + background-color: #e1e4e1; + border-color: #708090; + border-width: 5px; +} + +$theme.sc-split-divider-view { + background-color: #B0C4DE; +} + +$theme.footprint-logo-section-view { + background-color: #D0D0D0; + border-color: #D8D8D8; + border-width: 2px; +} + +$theme.footprint-right-logo-title-view { + font-size: 14; + font-weight: 700; + line-height: 24px; + text-align: center; +} + +$theme.footprint-left-city-title-view { + font-size: 14; + font-weight: 700; +} + +$theme.footprint-left-city-content-view { + font-size: 14; + font-weight: 400; +} + +$theme.footprint-left-county-title-view { + font-size: 14; + font-weight: 700; +} + +$theme.footprint-left-county-content-view { + font-size: 14; + font-weight: 400; +} + +$theme.footprint-left-state-title-view { + font-size: 14; + font-weight: 700; +} + +$theme.footprint-left-state-content-view { + font-size: 14; + font-weight: 400; +} + +$theme.footprint-copyright-view { + background-color: #F0F0F0; + border-top-color: #B8B8B8; + border-top-width: 2px; +} + +$theme.footprint-feature-edit-view { + background-color: #C0C0C0; +} + +$theme.footprint-layer-group-view, $theme.footprint-scenario-group, $theme.footprint-built-form-group-view { + top: 0; bottom: 0; left: 0; right: 0; + background-color: #E8E8E8; + border-top-width: 1px; + border-color: #A0A0A0; + border-bottom-width: 1px; + $theme.footprint-layer-group-label-view { + left: 34px; + } + $theme.footprint-built-form-group-label-view { + left: 25px; + line-height: 18px; + } +} +$theme.footprint-built-form-item { + $theme.footprint-built-form-item-label-view { + left: 25px; + line-height: 20px; + } + $theme.footprint-medium-color { + top: 2px; bottom: 2px; left: 5px; width: 16px; + } +} + +$theme.results-chart-example-container { + border-right-color: #707070; +} + +$theme.footprint.sc-view.chart-view{ + background-color: #f2f1ef; +} + +$theme.footprint.results-chart-title { + text-align: center; + font-weight: bold; + line-height: 25px; +} + +$theme.footprint.result-toggle-view { + font-size: 9px; + font-weight: 700; + line-height: 16px; +} + +$theme.footprint.sc-view.footprint-model-management-view { + background-color: #f2f1ef; +} + +$theme.footprint-dwelling-unit-summary-view{ + color: darkgreen; + font-size: 24px; +} + +$theme.footprint-employment-summary-view{ + color: darkgreen; + font-size: 24px; +} + + +$theme.footprint-single-family-small-title-view, $theme.footprint-attached-title-view, $theme.footprint-single-family-large-title-view, $theme.footprint-multifamily-title-view, $theme.footprint-retail-title-view, $theme.footprint-office-title-view, $theme.footprint-public-title-view, $theme.footprint-industrial-title-view{ + font-size: 11px; +} + + +$theme.result-legend-view{ + font-size: 9px; + +} + +$theme.footprint-add-building-pane{ + border-width: 3px; + border-color: #505050; +} + +$theme.footprint-summary-field-view{ + border-width: 1px; + border-color: #C0C0C0; +} + + +$theme.footprint-editable-input-view{ + border-width: 1px; + border-color: #C0C0C0; +} + +$theme.footprint-editable-building-use-types-view{ + border-width: 1px; + border-color: #C0C0C0; +} + +$theme.footprint-built-form-percent-list-scroll-view{ + border-width: 1px; + border-top: 1px; + border-left:0px; + border-color: #B0B0B0 ; +} + +$theme.slider-item-title{ + font-size: 9px; +} + +$theme.slider-item-symbol-label{ + font-size: 10px; +} + +$theme.slider-item-value-label{ + font-size: 11px; + background-color: whitesmoke; +} + +$theme.footprint-checkbox-item-title{ + font-size: 9px; +} + +$theme.footprint-layer-info-create-from-layer-selection-view { + $theme.footprint-checkbox-item-title{ + font-size: 11px; + } +} + +$theme.footprint-editable-clone-field-content-view{ + background-color: #f0f8ff; + border-width: 1px; + border-color: #B8B8B8 ; +} + +$theme.footprint-scenario-info-view, $theme.footprint-layer-info-view { + border-width: 2px; + border-color: #505050; + + $theme.footprint-info-title-view { + font-size: 1.3em; + font-weight: bold; + } + + $theme.form-info-overlay { + background-color: rgba(0, 0, 0, 0.7); + $theme.sc-label-view { + color: white; + text-align: center; + } + } + +} + +$theme.footprint-analytic-module-title-view{ + font-size: 1.3em; + font-weight: bold; +} + +$theme.footprint-manage-module-view{ + background-color: #f0f8ff; + border-bottom: 1px; + border-color: black; +} + +$theme.footprint-module-results-view{ + background-color: #f2f1ef; +} + +$theme.footprint-top-labeled-result-view{ + font-weight: bold; +} + +$theme.footprint-top-labeled-result-value-view{ + background-color: whitesmoke; +} + +$theme.footprint-top-labeled-result-title-view{ + background-color: #C0C0C0; +} +$theme.footprint-top-labeled-result-title-space-view{ + font-size: 10px; +} diff --git a/sproutcore/apps/fp/resources/stylesheets/leaflet.css b/sproutcore/apps/fp/resources/stylesheets/leaflet.css new file mode 100644 index 000000000..7e77da346 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/leaflet.css @@ -0,0 +1,467 @@ +/* required styles */ + +.leaflet-map-pane, +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow, +.leaflet-tile-pane, +.leaflet-tile-container, +.leaflet-overlay-pane, +.leaflet-shadow-pane, +.leaflet-marker-pane, +.leaflet-popup-pane, +.leaflet-overlay-pane svg, +.leaflet-zoom-box, +.leaflet-image-layer, +.leaflet-layer { + position: absolute; + left: 0; + top: 0; + } +.leaflet-container { + overflow: hidden; + -ms-touch-action: none; + } +.leaflet-tile, +.leaflet-marker-icon, +.leaflet-marker-shadow { + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + -webkit-user-drag: none; + } +.leaflet-marker-icon, +.leaflet-marker-shadow { + display: block; + } +/* map is broken in FF if you have max-width: 100% on tiles */ +.leaflet-container img { + max-width: none !important; + } +/* stupid Android 2 doesn't understand "max-width: none" properly */ +.leaflet-container img.leaflet-image-layer { + max-width: 15000px !important; + } +.leaflet-tile { + filter: inherit; + visibility: hidden; + } +.leaflet-tile-loaded { + visibility: inherit; + } +.leaflet-zoom-box { + width: 0; + height: 0; + } +/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */ +.leaflet-overlay-pane svg { + -moz-user-select: none; + } + +.leaflet-tile-pane { z-index: 2; } +.leaflet-objects-pane { z-index: 3; } +.leaflet-overlay-pane { z-index: 4; } +.leaflet-shadow-pane { z-index: 5; } +.leaflet-marker-pane { z-index: 6; } +.leaflet-popup-pane { z-index: 7; } + + +/* control positioning */ + +.leaflet-control { + position: relative; + z-index: 7; + pointer-events: auto; + } +.leaflet-top, +.leaflet-bottom { + position: absolute; + z-index: 1000; + pointer-events: none; + } +.leaflet-top { + top: 0; + } +.leaflet-right { + right: 0; + } +.leaflet-bottom { + bottom: 0; + } +.leaflet-left { + left: 0; + } +.leaflet-control { + float: left; + clear: both; + } +.leaflet-right .leaflet-control { + float: right; + } +.leaflet-top .leaflet-control { + margin-top: 10px; + } +.leaflet-bottom .leaflet-control { + margin-bottom: 10px; + } +.leaflet-left .leaflet-control { + margin-left: 10px; + } +.leaflet-right .leaflet-control { + margin-right: 10px; + } + + +/* zoom and fade animations */ + +.leaflet-fade-anim .leaflet-tile, +.leaflet-fade-anim .leaflet-popup { + opacity: 0; + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; + } +.leaflet-fade-anim .leaflet-tile-loaded, +.leaflet-fade-anim .leaflet-map-pane .leaflet-popup { + opacity: 1; + } + +.leaflet-zoom-anim .leaflet-zoom-animated { + -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1); + -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1); + -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1); + transition: transform 0.25s cubic-bezier(0,0,0.25,1); + } +.leaflet-zoom-anim .leaflet-tile, +.leaflet-pan-anim .leaflet-tile, +.leaflet-touching .leaflet-zoom-animated { + -webkit-transition: none; + -moz-transition: none; + -o-transition: none; + transition: none; + } + +.leaflet-zoom-anim .leaflet-zoom-hide { + visibility: hidden; + } + + +/* cursors */ + +.leaflet-clickable { + cursor: pointer; + } +.leaflet-container { + cursor: -webkit-grab; + cursor: -moz-grab; + } +.leaflet-popup-pane, +.leaflet-control { + cursor: auto; + } +.leaflet-dragging, +.leaflet-dragging .leaflet-clickable, +.leaflet-dragging .leaflet-container { + cursor: move; + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + } + + +/* visual tweaks */ + +.leaflet-container { + background: #ddd; + outline: 0; + } +.leaflet-container a { + color: #0078A8; + } +.leaflet-container a.leaflet-active { + outline: 2px solid orange; + } +.leaflet-zoom-box { + border: 2px dotted #05f; + background: white; + opacity: 0.5; + } + + +/* general typography */ +.leaflet-container { + font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif; + } + + +/* general toolbar styles */ + +.leaflet-bar { + box-shadow: 0 1px 7px rgba(0,0,0,0.65); + -webkit-border-radius: 4px; + border-radius: 4px; + } +.leaflet-bar a, .leaflet-bar a:hover { + background-color: #fff; + border-bottom: 1px solid #ccc; + width: 26px; + height: 26px; + line-height: 26px; + display: block; + text-align: center; + text-decoration: none; + color: black; + } +.leaflet-bar a, +.leaflet-control-layers-toggle { + background-position: 50% 50%; + background-repeat: no-repeat; + display: block; + } +.leaflet-bar a:hover { + background-color: #f4f4f4; + } +.leaflet-bar a:first-child { + -webkit-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; + } +.leaflet-bar a:last-child { + -webkit-border-bottom-left-radius: 4px; + border-bottom-left-radius: 4px; + -webkit-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + border-bottom: none; + } +.leaflet-bar a.leaflet-disabled { + cursor: default; + background-color: #f4f4f4; + color: #bbb; + } + +.leaflet-touch .leaflet-bar { + -webkit-border-radius: 10px; + border-radius: 10px; + } +.leaflet-touch .leaflet-bar a { + width: 30px; + height: 30px; + } +.leaflet-touch .leaflet-bar a:first-child { + -webkit-border-top-left-radius: 7px; + border-top-left-radius: 7px; + -webkit-border-top-right-radius: 7px; + border-top-right-radius: 7px; + } +.leaflet-touch .leaflet-bar a:last-child { + -webkit-border-bottom-left-radius: 7px; + border-bottom-left-radius: 7px; + -webkit-border-bottom-right-radius: 7px; + border-bottom-right-radius: 7px; + border-bottom: none; + } + + +/* zoom control */ + +.leaflet-control-zoom-in { + font: bold 18px 'Lucida Console', Monaco, monospace; + } +.leaflet-control-zoom-out { + font: bold 22px 'Lucida Console', Monaco, monospace; + } + +.leaflet-touch .leaflet-control-zoom-in { + font-size: 22px; + line-height: 30px; + } +.leaflet-touch .leaflet-control-zoom-out { + font-size: 28px; + line-height: 30px; + } + + +/* layers control */ + +.leaflet-control-layers { + box-shadow: 0 1px 7px rgba(0,0,0,0.4); + background: #f8f8f9; + -webkit-border-radius: 5px; + border-radius: 5px; + } +.leaflet-control-layers-toggle { + background-image: url(images/layers.png); + width: 36px; + height: 36px; + } +.leaflet-retina .leaflet-control-layers-toggle { + background-image: url(images/layers-2x.png); + background-size: 26px 26px; + } +.leaflet-touch .leaflet-control-layers-toggle { + width: 44px; + height: 44px; + } +.leaflet-control-layers .leaflet-control-layers-list, +.leaflet-control-layers-expanded .leaflet-control-layers-toggle { + display: none; + } +.leaflet-control-layers-expanded .leaflet-control-layers-list { + display: block; + position: relative; + } +.leaflet-control-layers-expanded { + padding: 6px 10px 6px 6px; + color: #333; + background: #fff; + } +.leaflet-control-layers-selector { + margin-top: 2px; + position: relative; + top: 1px; + } +.leaflet-control-layers label { + display: block; + } +.leaflet-control-layers-separator { + height: 0; + border-top: 1px solid #ddd; + margin: 5px -10px 5px -6px; + } + + +/* attribution and scale controls */ + +.leaflet-container .leaflet-control-attribution { + background-color: rgba(255, 255, 255, 0.7); + box-shadow: 0 0 5px #bbb; + margin: 0; + } +.leaflet-control-attribution, +.leaflet-control-scale-line { + padding: 0 5px; + color: #333; + } +.leaflet-container .leaflet-control-attribution, +.leaflet-container .leaflet-control-scale { + font-size: 11px; + } +.leaflet-left .leaflet-control-scale { + margin-left: 5px; + } +.leaflet-bottom .leaflet-control-scale { + margin-bottom: 5px; + } +.leaflet-control-scale-line { + border: 2px solid #777; + border-top: none; + color: black; + line-height: 1.1; + padding: 2px 5px 1px; + font-size: 11px; + text-shadow: 1px 1px 1px #fff; + background-color: rgba(255, 255, 255, 0.5); + box-shadow: 0 -1px 5px rgba(0, 0, 0, 0.2); + white-space: nowrap; + overflow: hidden; + } +.leaflet-control-scale-line:not(:first-child) { + border-top: 2px solid #777; + border-bottom: none; + margin-top: -2px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + } +.leaflet-control-scale-line:not(:first-child):not(:last-child) { + border-bottom: 2px solid #777; + } + +.leaflet-touch .leaflet-control-attribution, +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + box-shadow: none; + } +.leaflet-touch .leaflet-control-layers, +.leaflet-touch .leaflet-bar { + border: 4px solid rgba(0,0,0,0.3); + } + + +/* popup */ + +.leaflet-popup { + position: absolute; + text-align: center; + } +.leaflet-popup-content-wrapper { + padding: 1px; + text-align: left; + -webkit-border-radius: 12px; + border-radius: 12px; + } +.leaflet-popup-content { + margin: 13px 19px; + line-height: 1.4; + } +.leaflet-popup-content p { + margin: 18px 0; + } +.leaflet-popup-tip-container { + margin: 0 auto; + width: 40px; + height: 20px; + position: relative; + overflow: hidden; + } +.leaflet-popup-tip { + width: 17px; + height: 17px; + padding: 1px; + + margin: -10px auto 0; + + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + } +.leaflet-popup-content-wrapper, .leaflet-popup-tip { + background: white; + + box-shadow: 0 3px 14px rgba(0,0,0,0.4); + } +.leaflet-container a.leaflet-popup-close-button { + position: absolute; + top: 0; + right: 0; + padding: 4px 4px 0 0; + text-align: center; + width: 18px; + height: 14px; + font: 16px/14px Tahoma, Verdana, sans-serif; + color: #c3c3c3; + text-decoration: none; + font-weight: bold; + background: transparent; + } +.leaflet-container a.leaflet-popup-close-button:hover { + color: #999; + } +.leaflet-popup-scrolled { + overflow: auto; + border-bottom: 1px solid #ddd; + border-top: 1px solid #ddd; + } + + +/* div icon */ + +.leaflet-div-icon { + background: #fff; + border: 1px solid #666; + } +.leaflet-editing-icon { + -webkit-border-radius: 2px; + border-radius: 2px; + } diff --git a/sproutcore/apps/fp/resources/stylesheets/list_views/layer.css b/sproutcore/apps/fp/resources/stylesheets/list_views/layer.css new file mode 100644 index 000000000..e69de29bb diff --git a/sproutcore/apps/fp/resources/stylesheets/list_views/result.css b/sproutcore/apps/fp/resources/stylesheets/list_views/result.css new file mode 100644 index 000000000..8b06ae07c --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/list_views/result.css @@ -0,0 +1,4 @@ +$theme.horizontal-list-view { + background-color:#d0dae3; +} + diff --git a/sproutcore/apps/fp/resources/stylesheets/panels.css b/sproutcore/apps/fp/resources/stylesheets/panels.css new file mode 100644 index 000000000..6f12ce3d5 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/panels.css @@ -0,0 +1,3 @@ +$theme.footprint-info-pane.opaque { + opacity: 0.9; +} \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/stylesheets/presentation/map/footprint_map.css b/sproutcore/apps/fp/resources/stylesheets/presentation/map/footprint_map.css new file mode 100644 index 000000000..9d078a5c4 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/presentation/map/footprint_map.css @@ -0,0 +1,112 @@ +.pallete { + position: relative; + max-width: 100px; + float: right; + z-index: 100; + outline: black solid; +} + +.builtformbutton { + margin: 0; + padding: 1px; + text-align: center; +} + +.parcels { + fill-opacity: .7; + stroke-width: .5; + stroke: grey; + stroke-opacity: .4; +} + +.parcel_geometry:hover{ + fill-opacity: .5; + fill:orange !important; +} + +.selected{ + fill: yellow !important; + opacity: 1 !important; + stroke: red; + stroke-width: 3; +} + +#parcel_base { + opacity: .8; +} + +.paint-tool .extent { + stroke: #000; + stroke-width: 1.5px; + fill: #000; + fill-opacity: 0.3; +} + +#layerswitcher { + background-color: black; + margin-right: 0; + padding: 1.5em; + color: white; + float: right; + position: absolute; + z-index: 100; +} +#identify-popup { + z-index: 50; + margin-left: 40%; + margin-top: 20%; + max-width: 300px; + max-height: 300px; + position: absolute; + background: #000000; + opacity: .9; + border: solid thin black; +} +#identify-popup p { + font-size: 12px; + text-align: right; + color: grey; + margin: 10px; +} + +#identify-popup h5 { + font-size: 15px; + color: #fff; + margin: 20px; +} +.tool-selector { + position: absolute; + background: #000; + opacity: .9; + color: #fff; + padding-right: .5em; + padding-left: .5em; + z-index: 50; +} + +#map-interface { + position: absolute; + margin-top: -1; + margin-left: -1; +} + +.footprint-map-rezoom-to-extent-button { + padding-left: 2px; + padding-right: 2px; + .sc-button-label.sc-regular-size { + padding-left: 0px; + padding-right: 0px; + } +} + +/* FAKE CSS FOR BUILT FORMS */ + +.ptid-1 { fill: #1EB068; } +.ptid-2 { fill: #EBE07A; } +.ptid-3 { fill: #E61414; } +.ptid-4 { fill: #8764A6; } +.ptid-5 { fill: #E6CCFF; } +.ptid-11 { fill: #9551ae; } + +.ptid-null { fill: #f8fcff; + opacity: .1;} diff --git a/sproutcore/apps/fp/resources/stylesheets/presentation/map/footprint_polymaps.css b/sproutcore/apps/fp/resources/stylesheets/presentation/map/footprint_polymaps.css new file mode 100644 index 000000000..7c7193cb6 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/presentation/map/footprint_polymaps.css @@ -0,0 +1,23 @@ +.parcels { + fill-opacity: .2; + stroke-width: 1; + stroke: grey; + stroke-opacity: .4; +} + +.parcel_geometry:hover{ + fill-opacity: .5; + fill:orange !important; +} + +.selected{ + fill: yellow !important; + opacity: 1 !important; + stroke: red; + stroke-width: 3; +} + +#parcel_base { + stroke: red !important; + opacity: .6; +} diff --git a/sproutcore/apps/fp/resources/stylesheets/presentation/map/polymaps.css b/sproutcore/apps/fp/resources/stylesheets/presentation/map/polymaps.css new file mode 100644 index 000000000..26e20a095 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/presentation/map/polymaps.css @@ -0,0 +1,112 @@ +svg { + display: block; + overflow: hidden; + width: 100%; + height: 100%; +} + +$theme.footprint-map-layer-toggle { + background-color: white; + opacity:.8 +} + +$theme.footprint-map-rezoom-to-project { + height: 15px; + width: 15px; + @include slice('../../../images/zoom_to_extent.png') +} + +#copy { + position: absolute; + left: 0; + bottom: 4px; + + padding-left: 5px; + font: 9px sans-serif; + color: #fff; + cursor: default; +} + +#copy a { + color: #fff; +} + +.compass .back { + fill: #eee; + fill-opacity: .8; +} + +.compass .fore { + stroke: #999; + stroke-width: 1.5px; +} + +.compass rect.back.fore { + fill: #999; + fill-opacity: .3; + stroke: #eee; + stroke-width: 1px; + shape-rendering: crispEdges; +} + +.compass .direction { + fill: none; +} + +.compass .chevron { + fill: none; + stroke: #999; + stroke-width: 5px; +} + +.compass .zoom .chevron { + stroke-width: 4px; +} + +.compass .active .chevron, .compass .chevron.active { + stroke: #fff; +} + +.compass.active .active .direction { + fill: #999; +} + +.layer path { + fill: none; + vector-effect: non-scaling-stroke; +} + +/* TODO layer specific stuff belongs elsewhere */ + +#county path { + stroke: rgb(192, 192, 192); + stroke-opacity: .25; + shape-rendering: crispEdges; +} + +#state path { + stroke: #fff; + stroke-width: 1.5px; +} + +#copy { + color: #000; + opacity: .5; +} + +#copy a { + color: #000; +} + + + +#county\:02013,#county\:02016,#county\:02050,#county\:02060,#county\:02068,#county\:02070,#county\:02100,#county\:02122,#county\:02150,#county\:02164,#county\:02170,#county\:02180,#county\:02185,#county\:02188,#county\:02201,#county\:02220,#county\:02232,#county\:02240,#county\:02261,#county\:02270,#county\:02280,#county\:02282,#county\:02290,#county\:04011,#county\:04012,#county\:06003,#county\:06027,#county\:06049,#county\:06051,#county\:06091,#county\:06105,#county\:08009,#county\:08011,#county\:08017,#county\:08023,#county\:08027,#county\:08033,#county\:08051,#county\:08053,#county\:08057,#county\:08061,#county\:08063,#county\:08071,#county\:08073,#county\:08079,#county\:08081,#county\:08103,#county\:08109,#county\:08111,#county\:08121,#county\:08125,#county\:16003,#county\:16015,#county\:16023,#county\:16025,#county\:16029,#county\:16033,#county\:16035,#county\:16037,#county\:16049,#county\:16059,#county\:16063,#county\:16071,#county\:16073,#county\:16085,#county\:20007,#county\:20017,#county\:20023,#county\:20025,#county\:20033,#county\:20039,#county\:20063,#county\:20065,#county\:20071,#county\:20075,#county\:20083,#county\:20089,#county\:20097,#county\:20101,#county\:20109,#county\:20119,#county\:20129,#county\:20135,#county\:20153,#county\:20179,#county\:20187,#county\:20195,#county\:20199,#county\:20203,#county\:23021,#county\:26083,#county\:27031,#county\:27069,#county\:27071,#county\:27077,#county\:30001,#county\:30003,#county\:30005,#county\:30007,#county\:30009,#county\:30011,#county\:30015,#county\:30017,#county\:30019,#county\:30021,#county\:30025,#county\:30027,#county\:30033,#county\:30035,#county\:30037,#county\:30039,#county\:30045,#county\:30051,#county\:30055,#county\:30057,#county\:30059,#county\:30061,#county\:30065,#county\:30069,#county\:30071,#county\:30073,#county\:30075,#county\:30077,#county\:30079,#county\:30083,#county\:30085,#county\:30087,#county\:30089,#county\:30091,#county\:30095,#county\:30097,#county\:30099,#county\:30101,#county\:30103,#county\:30105,#county\:30107,#county\:30109,#county\:31005,#county\:31007,#county\:31009,#county\:31015,#county\:31017,#county\:31029,#county\:31031,#county\:31041,#county\:31049,#county\:31057,#county\:31063,#county\:31069,#county\:31071,#county\:31073,#county\:31075,#county\:31077,#county\:31085,#county\:31087,#county\:31089,#county\:31091,#county\:31103,#county\:31105,#county\:31113,#county\:31115,#county\:31117,#county\:31123,#county\:31135,#county\:31149,#county\:31161,#county\:31165,#county\:31171,#county\:31183,#county\:32007,#county\:32009,#county\:32011,#county\:32013,#county\:32015,#county\:32017,#county\:32021,#county\:32023,#county\:32027,#county\:32033,#county\:35003,#county\:35007,#county\:35011,#county\:35019,#county\:35021,#county\:35023,#county\:35027,#county\:35033,#county\:35037,#county\:35051,#county\:35053,#county\:35059,#county\:36041,#county\:38001,#county\:38007,#county\:38009,#county\:38011,#county\:38013,#county\:38019,#county\:38023,#county\:38025,#county\:38027,#county\:38029,#county\:38033,#county\:38037,#county\:38039,#county\:38041,#county\:38043,#county\:38045,#county\:38047,#county\:38049,#county\:38051,#county\:38053,#county\:38055,#county\:38061,#county\:38063,#county\:38065,#county\:38069,#county\:38075,#county\:38083,#county\:38085,#county\:38087,#county\:38091,#county\:38095,#county\:38103,#county\:40007,#county\:40025,#county\:40043,#county\:40045,#county\:40059,#county\:40129,#county\:41021,#county\:41023,#county\:41025,#county\:41037,#county\:41045,#county\:41055,#county\:41063,#county\:41069,#county\:46003,#county\:46007,#county\:46017,#county\:46019,#county\:46021,#county\:46025,#county\:46031,#county\:46033,#county\:46041,#county\:46045,#county\:46047,#county\:46049,#county\:46053,#county\:46055,#county\:46059,#county\:46063,#county\:46069,#county\:46071,#county\:46073,#county\:46075,#county\:46085,#county\:46089,#county\:46095,#county\:46105,#county\:46107,#county\:46111,#county\:46117,#county\:46119,#county\:46123,#county\:46137,#county\:48011,#county\:48023,#county\:48033,#county\:48043,#county\:48045,#county\:48079,#county\:48081,#county\:48087,#county\:48095,#county\:48101,#county\:48105,#county\:48109,#county\:48111,#county\:48125,#county\:48129,#county\:48137,#county\:48151,#county\:48155,#county\:48173,#county\:48191,#county\:48205,#county\:48211,#county\:48229,#county\:48235,#county\:48243,#county\:48247,#county\:48261,#county\:48263,#county\:48267,#county\:48269,#county\:48271,#county\:48283,#county\:48295,#county\:48301,#county\:48311,#county\:48319,#county\:48327,#county\:48345,#county\:48359,#county\:48371,#county\:48377,#county\:48383,#county\:48385,#county\:48393,#county\:48413,#county\:48417,#county\:48421,#county\:48431,#county\:48433,#county\:48435,#county\:48443,#county\:48447,#county\:48461,#county\:49001,#county\:49009,#county\:49013,#county\:49015,#county\:49017,#county\:49019,#county\:49023,#county\:49025,#county\:49027,#county\:49031,#county\:49033,#county\:49037,#county\:49055,#county\:53013,#county\:53019,#county\:53023,#county\:53043,#county\:56003,#county\:56007,#county\:56009,#county\:56011,#county\:56013,#county\:56017,#county\:56019,#county\:56023,#county\:56027,#county\:56029,#county\:56031,#county\:56035,#county\:56037,#county\:56039,#county\:56043,#county\:56045{fill:#FFF7F3} +#county\:02090,#county\:02110,#county\:02130,#county\:04001,#county\:04005,#county\:04007,#county\:04009,#county\:04015,#county\:04017,#county\:05013,#county\:05025,#county\:05039,#county\:05097,#county\:05101,#county\:05127,#county\:05129,#county\:06035,#county\:06043,#county\:06063,#county\:06093,#county\:08007,#county\:08021,#county\:08025,#county\:08039,#county\:08049,#county\:08055,#county\:08075,#county\:08083,#county\:08091,#county\:08093,#county\:08095,#county\:08099,#county\:08105,#county\:08107,#county\:08113,#county\:08115,#county\:12043,#county\:12067,#county\:12077,#county\:13007,#county\:13049,#county\:13065,#county\:13101,#county\:13259,#county\:13265,#county\:13307,#county\:15005,#county\:16007,#county\:16009,#county\:16013,#county\:16021,#county\:16031,#county\:16039,#county\:16043,#county\:16061,#county\:16077,#county\:16079,#county\:16081,#county\:16087,#county\:17151,#county\:19003,#county\:19159,#county\:19173,#county\:19185,#county\:20003,#county\:20019,#county\:20027,#county\:20029,#county\:20031,#county\:20047,#county\:20049,#county\:20053,#county\:20067,#county\:20069,#county\:20073,#county\:20077,#county\:20081,#county\:20093,#county\:20095,#county\:20105,#county\:20115,#county\:20117,#county\:20123,#county\:20127,#county\:20137,#county\:20141,#county\:20143,#county\:20145,#county\:20147,#county\:20151,#county\:20157,#county\:20163,#county\:20165,#county\:20167,#county\:20171,#county\:20181,#county\:20183,#county\:20185,#county\:20189,#county\:20193,#county\:20197,#county\:20201,#county\:20207,#county\:22023,#county\:22107,#county\:23003,#county\:23025,#county\:23029,#county\:26003,#county\:26013,#county\:26071,#county\:26095,#county\:26097,#county\:26131,#county\:26153,#county\:27001,#county\:27011,#county\:27021,#county\:27029,#county\:27051,#county\:27073,#county\:27075,#county\:27081,#county\:27087,#county\:27089,#county\:27101,#county\:27107,#county\:27125,#county\:27135,#county\:27155,#county\:27167,#county\:28055,#county\:28069,#county\:29005,#county\:29035,#county\:29041,#county\:29061,#county\:29075,#county\:29081,#county\:29087,#county\:29103,#county\:29129,#county\:29149,#county\:29153,#county\:29171,#county\:29179,#county\:29185,#county\:29197,#county\:29199,#county\:29203,#county\:29205,#county\:29211,#county\:29227,#county\:30023,#county\:30041,#county\:30043,#county\:30053,#county\:30067,#county\:31003,#county\:31011,#county\:31013,#county\:31027,#county\:31033,#county\:31035,#county\:31045,#county\:31051,#county\:31059,#county\:31061,#county\:31065,#county\:31083,#county\:31093,#county\:31097,#county\:31099,#county\:31101,#county\:31107,#county\:31111,#county\:31125,#county\:31129,#county\:31133,#county\:31139,#county\:31143,#county\:31163,#county\:31169,#county\:31175,#county\:31181,#county\:32001,#county\:32029,#county\:35005,#county\:35006,#county\:35015,#county\:35017,#county\:35025,#county\:35029,#county\:35031,#county\:35035,#county\:35039,#county\:35041,#county\:35047,#county\:35055,#county\:35057,#county\:37095,#county\:37177,#county\:38003,#county\:38005,#county\:38021,#county\:38031,#county\:38057,#county\:38059,#county\:38067,#county\:38071,#county\:38073,#county\:38077,#county\:38081,#county\:38093,#county\:38097,#county\:38099,#county\:38105,#county\:40003,#county\:40005,#county\:40011,#county\:40029,#county\:40033,#county\:40053,#county\:40055,#county\:40057,#county\:40067,#county\:40075,#county\:40093,#county\:40127,#county\:40139,#county\:40141,#county\:40149,#county\:40151,#county\:41001,#county\:41013,#county\:41015,#county\:41031,#county\:41035,#county\:41049,#county\:41061,#county\:41065,#county\:42053,#county\:46005,#county\:46009,#county\:46015,#county\:46023,#county\:46037,#county\:46039,#county\:46043,#county\:46051,#county\:46057,#county\:46061,#county\:46067,#county\:46077,#county\:46087,#county\:46091,#county\:46093,#county\:46097,#county\:46101,#county\:46109,#county\:46113,#county\:46115,#county\:46121,#county\:46125,#county\:46129,#county\:48003,#county\:48009,#county\:48017,#county\:48031,#county\:48047,#county\:48065,#county\:48069,#county\:48075,#county\:48077,#county\:48083,#county\:48103,#county\:48107,#county\:48117,#county\:48127,#county\:48131,#county\:48153,#county\:48163,#county\:48165,#county\:48169,#county\:48175,#county\:48193,#county\:48195,#county\:48197,#county\:48207,#county\:48237,#county\:48275,#county\:48289,#county\:48297,#county\:48305,#county\:48307,#county\:48317,#county\:48333,#county\:48335,#county\:48357,#county\:48369,#county\:48387,#county\:48389,#county\:48391,#county\:48399,#county\:48411,#county\:48429,#county\:48437,#county\:48445,#county\:48465,#county\:48475,#county\:48483,#county\:48495,#county\:48501,#county\:48505,#county\:48507,#county\:49003,#county\:49007,#county\:49021,#county\:49029,#county\:49039,#county\:49041,#county\:49045,#county\:49047,#county\:49051,#county\:50009,#county\:51017,#county\:51091,#county\:53001,#county\:53031,#county\:53039,#county\:53047,#county\:53051,#county\:53059,#county\:54071,#county\:54075,#county\:55007,#county\:55037,#county\:55041,#county\:55051,#county\:55078,#county\:55099,#county\:55113,#county\:56001,#county\:56005,#county\:56015,#county\:56025,#county\:56033,#county\:56041{fill:#FDE0DD} +#county\:01011,#county\:01023,#county\:01025,#county\:01027,#county\:01035,#county\:01037,#county\:01041,#county\:01063,#county\:01085,#county\:01091,#county\:01099,#county\:01105,#county\:01107,#county\:01119,#county\:01129,#county\:01131,#county\:04003,#county\:04025,#county\:05001,#county\:05011,#county\:05017,#county\:05041,#county\:05043,#county\:05049,#county\:05061,#county\:05065,#county\:05073,#county\:05077,#county\:05087,#county\:05095,#county\:05099,#county\:05105,#county\:05109,#county\:05113,#county\:05117,#county\:05137,#county\:05141,#county\:05147,#county\:05149,#county\:06011,#county\:06021,#county\:06103,#county\:06109,#county\:08003,#county\:08015,#county\:08019,#county\:08029,#county\:08045,#county\:08065,#county\:08085,#county\:08087,#county\:08089,#county\:08097,#county\:12013,#county\:12029,#county\:12037,#county\:12045,#county\:12065,#county\:12123,#county\:13003,#county\:13037,#county\:13061,#county\:13099,#county\:13125,#county\:13141,#county\:13165,#county\:13197,#county\:13201,#county\:13239,#county\:13243,#county\:13249,#county\:13251,#county\:13263,#county\:13269,#county\:13301,#county\:13309,#county\:13315,#county\:13317,#county\:13319,#county\:16011,#county\:16017,#county\:16041,#county\:16047,#county\:16051,#county\:17009,#county\:17013,#county\:17059,#county\:17065,#county\:17071,#county\:17079,#county\:17149,#county\:17169,#county\:17171,#county\:17175,#county\:17191,#county\:18007,#county\:18171,#county\:19001,#county\:19005,#county\:19009,#county\:19025,#county\:19035,#county\:19039,#county\:19043,#county\:19047,#county\:19051,#county\:19053,#county\:19069,#county\:19071,#county\:19073,#county\:19077,#county\:19081,#county\:19085,#county\:19089,#county\:19091,#county\:19093,#county\:19107,#county\:19109,#county\:19117,#county\:19119,#county\:19131,#county\:19133,#county\:19135,#county\:19143,#county\:19147,#county\:19151,#county\:19161,#county\:19165,#county\:19177,#county\:19195,#county\:20011,#county\:20013,#county\:20041,#county\:20043,#county\:20085,#county\:20107,#county\:20131,#county\:20139,#county\:20149,#county\:20159,#county\:20191,#county\:20205,#county\:21057,#county\:21105,#county\:21189,#county\:21201,#county\:22013,#county\:22021,#county\:22025,#county\:22027,#county\:22035,#county\:22059,#county\:22065,#county\:22127,#county\:23007,#county\:26001,#county\:26053,#county\:26085,#county\:26109,#county\:26119,#county\:26135,#county\:26141,#county\:27005,#county\:27007,#county\:27023,#county\:27033,#county\:27043,#county\:27045,#county\:27057,#county\:27061,#county\:27063,#county\:27113,#county\:27115,#county\:27117,#county\:27119,#county\:27121,#county\:27127,#county\:27129,#county\:27133,#county\:27149,#county\:27151,#county\:27173,#county\:28005,#county\:28009,#county\:28015,#county\:28019,#county\:28021,#county\:28037,#county\:28041,#county\:28063,#county\:28103,#county\:28111,#county\:28125,#county\:28135,#county\:28143,#county\:28155,#county\:28157,#county\:29011,#county\:29013,#county\:29015,#county\:29017,#county\:29025,#county\:29033,#county\:29045,#county\:29057,#county\:29065,#county\:29067,#county\:29079,#county\:29085,#county\:29089,#county\:29093,#county\:29111,#county\:29115,#county\:29121,#county\:29123,#county\:29125,#county\:29137,#county\:29139,#county\:29151,#county\:29173,#county\:29181,#county\:29215,#county\:29217,#county\:29223,#county\:30029,#county\:30047,#county\:30049,#county\:30081,#county\:31021,#county\:31023,#county\:31039,#county\:31047,#county\:31081,#county\:31095,#county\:31121,#county\:31127,#county\:31137,#county\:31145,#county\:31147,#county\:31151,#county\:31167,#county\:31173,#county\:31179,#county\:32019,#county\:33007,#county\:35043,#county\:35045,#county\:36031,#county\:36049,#county\:37103,#county\:38079,#county\:38089,#county\:40009,#county\:40015,#county\:40023,#county\:40035,#county\:40061,#county\:40063,#county\:40069,#county\:40073,#county\:40077,#county\:40085,#county\:40089,#county\:40103,#county\:40105,#county\:40107,#county\:40113,#county\:40153,#county\:41019,#county\:41057,#county\:41059,#county\:42023,#county\:42105,#county\:42113,#county\:46013,#county\:46065,#county\:46079,#county\:47135,#county\:47175,#county\:47181,#county\:48019,#county\:48035,#county\:48059,#county\:48089,#county\:48093,#county\:48115,#county\:48119,#county\:48123,#county\:48133,#county\:48145,#county\:48149,#county\:48161,#county\:48171,#county\:48177,#county\:48179,#county\:48225,#county\:48239,#county\:48253,#county\:48255,#county\:48279,#county\:48285,#county\:48293,#county\:48299,#county\:48331,#county\:48337,#county\:48341,#county\:48351,#county\:48353,#county\:48395,#county\:48403,#county\:48405,#county\:48415,#county\:48455,#county\:48457,#county\:48463,#county\:48487,#county\:48503,#county\:49043,#county\:51021,#county\:51045,#county\:51097,#county\:51181,#county\:53007,#county\:53017,#county\:53037,#county\:53049,#county\:53065,#county\:53069,#county\:53075,#county\:54017,#county\:54021,#county\:54023,#county\:54031,#county\:54085,#county\:54093,#county\:54101,#county\:55003,#county\:55011,#county\:55013,#county\:55053,#county\:55067,#county\:55107,#county\:55119,#county\:55125,#county\:55129{fill:#FCC5C0} +#county\:01005,#county\:01007,#county\:01013,#county\:01029,#county\:01057,#county\:01065,#county\:01067,#county\:01075,#county\:04021,#county\:04023,#county\:04027,#county\:05003,#county\:05019,#county\:05021,#county\:05027,#county\:05037,#county\:05047,#county\:05053,#county\:05057,#county\:05067,#county\:05071,#county\:05075,#county\:05079,#county\:05081,#county\:05083,#county\:05089,#county\:05111,#county\:05121,#county\:05133,#county\:05135,#county\:06015,#county\:06023,#county\:06025,#county\:06045,#county\:08037,#county\:08043,#county\:08047,#county\:08067,#county\:08077,#county\:12047,#county\:12051,#county\:12075,#county\:12079,#county\:12133,#county\:13001,#county\:13005,#county\:13019,#county\:13025,#county\:13027,#county\:13033,#county\:13093,#county\:13107,#county\:13155,#county\:13159,#county\:13163,#county\:13167,#county\:13183,#county\:13191,#county\:13193,#county\:13209,#county\:13221,#county\:13271,#county\:13273,#county\:13283,#county\:13287,#county\:13289,#county\:13303,#county\:16045,#county\:16053,#county\:16057,#county\:16067,#county\:16083,#county\:17023,#county\:17025,#county\:17035,#county\:17045,#county\:17047,#county\:17051,#county\:17053,#county\:17061,#county\:17067,#county\:17069,#county\:17075,#county\:17123,#county\:17125,#county\:17131,#county\:17173,#county\:17187,#county\:17189,#county\:17193,#county\:18025,#county\:18101,#county\:18111,#county\:18131,#county\:19007,#county\:19011,#county\:19021,#county\:19023,#county\:19029,#county\:19031,#county\:19037,#county\:19041,#county\:19055,#county\:19063,#county\:19065,#county\:19067,#county\:19075,#county\:19079,#county\:19083,#county\:19095,#county\:19097,#county\:19105,#county\:19115,#county\:19121,#county\:19129,#county\:19137,#county\:19141,#county\:19145,#county\:19149,#county\:19157,#county\:19171,#county\:19175,#county\:19183,#county\:19189,#county\:19191,#county\:19197,#county\:20001,#county\:20009,#county\:20035,#county\:20051,#county\:20055,#county\:20057,#county\:20087,#county\:20099,#county\:20113,#county\:20133,#county\:20175,#county\:21007,#county\:21025,#county\:21027,#county\:21031,#county\:21039,#county\:21045,#county\:21055,#county\:21063,#county\:21131,#county\:21135,#county\:21139,#county\:21165,#county\:21169,#county\:21171,#county\:21181,#county\:21187,#county\:21219,#county\:21221,#county\:21229,#county\:21237,#county\:22003,#county\:22011,#county\:22029,#county\:22031,#county\:22041,#county\:22043,#county\:22049,#county\:22069,#county\:22075,#county\:22081,#county\:22085,#county\:22091,#county\:22111,#county\:22123,#county\:23009,#county\:23017,#county\:26033,#county\:26039,#county\:26041,#county\:26043,#county\:26061,#county\:26079,#county\:26103,#county\:26113,#county\:27055,#county\:27065,#county\:27083,#county\:27091,#county\:27097,#county\:27105,#county\:27111,#county\:27137,#county\:27143,#county\:27153,#county\:27159,#county\:27165,#county\:28007,#county\:28013,#county\:28023,#county\:28051,#county\:28053,#county\:28061,#county\:28065,#county\:28077,#county\:28079,#county\:28097,#county\:28119,#county\:28129,#county\:28131,#county\:28153,#county\:28159,#county\:28161,#county\:28163,#county\:29039,#county\:29053,#county\:29055,#county\:29059,#county\:29063,#county\:29073,#county\:29083,#county\:29117,#county\:29133,#county\:29135,#county\:29141,#county\:29143,#county\:29147,#county\:29163,#county\:29186,#county\:29195,#county\:29207,#county\:29221,#county\:29229,#county\:30013,#county\:30031,#county\:31037,#county\:31067,#county\:31131,#county\:31155,#county\:31159,#county\:31185,#county\:35009,#county\:36025,#county\:36033,#county\:37015,#county\:37029,#county\:37073,#county\:37075,#county\:37173,#county\:38101,#county\:39111,#county\:39115,#county\:39121,#county\:39163,#county\:40039,#county\:40049,#county\:40065,#county\:40079,#county\:40081,#county\:40091,#county\:40095,#county\:40099,#county\:40117,#county\:40121,#county\:42057,#county\:45005,#county\:45029,#county\:45039,#county\:45053,#county\:45065,#county\:46011,#county\:46027,#county\:46081,#county\:46103,#county\:46127,#county\:47007,#county\:47027,#county\:47039,#county\:47049,#county\:47067,#county\:47085,#county\:47087,#county\:47137,#county\:47161,#county\:48013,#county\:48015,#county\:48051,#county\:48053,#county\:48067,#county\:48143,#county\:48147,#county\:48159,#county\:48185,#county\:48217,#county\:48219,#county\:48233,#county\:48259,#county\:48273,#county\:48281,#county\:48287,#county\:48313,#county\:48315,#county\:48321,#county\:48325,#county\:48363,#county\:48365,#county\:48419,#county\:48489,#county\:51005,#county\:51007,#county\:51025,#county\:51029,#county\:51037,#county\:51049,#county\:51111,#county\:51125,#county\:51157,#county\:51163,#county\:51175,#county\:51183,#county\:53003,#county\:53025,#county\:53027,#county\:53041,#county\:54007,#county\:54013,#county\:54015,#county\:54025,#county\:54027,#county\:54063,#county\:54083,#county\:54087,#county\:54089,#county\:54105,#county\:55001,#county\:55019,#county\:55023,#county\:55031,#county\:55049,#county\:55057,#county\:55065,#county\:55069,#county\:55075,#county\:55077,#county\:55083,#county\:55085,#county\:55091,#county\:55103,#county\:55123,#county\:56021{fill:#FA9FB5} +#county\:01019,#county\:01039,#county\:01047,#county\:01053,#county\:01059,#county\:01061,#county\:01071,#county\:01079,#county\:01087,#county\:01093,#county\:01109,#county\:01111,#county\:01133,#county\:05015,#county\:05023,#county\:05029,#county\:05059,#county\:05063,#county\:05103,#county\:05107,#county\:05123,#county\:05139,#county\:06009,#county\:06033,#county\:06069,#county\:06089,#county\:08117,#county\:08119,#county\:08123,#county\:12003,#county\:12027,#county\:12041,#county\:12049,#county\:12059,#county\:12093,#county\:12129,#county\:12131,#county\:13043,#county\:13079,#county\:13087,#county\:13091,#county\:13133,#county\:13149,#county\:13161,#county\:13173,#county\:13181,#county\:13199,#county\:13205,#county\:13211,#county\:13229,#county\:13235,#county\:13241,#county\:13253,#county\:13267,#county\:13299,#county\:13305,#county\:13321,#county\:15001,#county\:16019,#county\:16069,#county\:16075,#county\:17003,#county\:17005,#county\:17011,#county\:17015,#county\:17017,#county\:17021,#county\:17033,#county\:17039,#county\:17041,#county\:17057,#county\:17085,#county\:17087,#county\:17101,#county\:17103,#county\:17105,#county\:17107,#county\:17129,#county\:17135,#county\:17139,#county\:17147,#county\:17153,#county\:17155,#county\:17159,#county\:17181,#county\:18013,#county\:18045,#county\:18117,#county\:18121,#county\:18123,#county\:18125,#county\:18139,#county\:18153,#county\:18155,#county\:18161,#county\:18181,#county\:19015,#county\:19019,#county\:19027,#county\:19059,#county\:19087,#county\:19101,#county\:19123,#county\:19167,#county\:20005,#county\:20015,#county\:20021,#county\:20059,#county\:20111,#county\:20121,#county\:21001,#county\:21011,#county\:21023,#county\:21033,#county\:21053,#county\:21061,#county\:21069,#county\:21075,#county\:21085,#county\:21087,#county\:21091,#county\:21099,#county\:21109,#county\:21119,#county\:21127,#county\:21129,#county\:21141,#county\:21143,#county\:21147,#county\:21149,#county\:21153,#county\:21175,#county\:21183,#county\:21225,#county\:21231,#county\:21233,#county\:22009,#county\:22037,#county\:22053,#county\:22067,#county\:22077,#county\:22083,#county\:22113,#county\:22115,#county\:22125,#county\:23019,#county\:23027,#county\:24023,#county\:26009,#county\:26011,#county\:26019,#county\:26031,#county\:26063,#county\:26069,#county\:26101,#county\:26127,#county\:26129,#county\:26133,#county\:26137,#county\:26143,#county\:26151,#county\:27015,#county\:27017,#county\:27027,#county\:27039,#county\:27047,#county\:27093,#county\:27095,#county\:27157,#county\:27161,#county\:28011,#county\:28017,#county\:28029,#county\:28031,#county\:28039,#county\:28057,#county\:28091,#county\:28093,#county\:28095,#county\:28099,#county\:28101,#county\:28107,#county\:28123,#county\:28127,#county\:28133,#county\:28139,#county\:28141,#county\:28147,#county\:29001,#county\:29003,#county\:29007,#county\:29009,#county\:29027,#county\:29049,#county\:29091,#county\:29105,#county\:29119,#county\:29131,#county\:29155,#county\:29157,#county\:29167,#county\:29177,#county\:30063,#county\:30093,#county\:30111,#county\:31019,#county\:31025,#county\:31141,#county\:31157,#county\:31177,#county\:33003,#county\:33009,#county\:35013,#county\:36003,#county\:36043,#county\:36089,#county\:37005,#county\:37007,#county\:37017,#county\:37043,#county\:37115,#county\:37131,#county\:37137,#county\:37141,#county\:37143,#county\:37185,#county\:37187,#county\:38015,#county\:38035,#county\:39001,#county\:39067,#county\:39125,#county\:40001,#county\:40013,#county\:40041,#county\:40051,#county\:40083,#county\:40087,#county\:40123,#county\:40133,#county\:40137,#county\:41007,#county\:41011,#county\:41017,#county\:41027,#county\:41033,#county\:41041,#county\:41043,#county\:42009,#county\:42035,#county\:42047,#county\:42083,#county\:42117,#county\:42123,#county\:45009,#county\:45011,#county\:45017,#county\:45037,#county\:45049,#county\:45061,#county\:45081,#county\:45089,#county\:46029,#county\:46035,#county\:46083,#county\:46135,#county\:47005,#county\:47015,#county\:47017,#county\:47047,#county\:47055,#county\:47061,#county\:47069,#county\:47071,#county\:47075,#county\:47077,#county\:47081,#county\:47083,#county\:47095,#county\:47101,#county\:47109,#county\:47127,#county\:47129,#county\:47133,#county\:47139,#county\:47151,#county\:47153,#county\:48025,#county\:48049,#county\:48057,#county\:48071,#county\:48073,#county\:48097,#county\:48189,#county\:48223,#county\:48227,#county\:48241,#county\:48249,#county\:48265,#county\:48323,#county\:48349,#county\:48373,#county\:48379,#county\:48407,#county\:48425,#county\:48427,#county\:48477,#county\:48481,#county\:48493,#county\:49053,#county\:50001,#county\:50005,#county\:50015,#county\:50017,#county\:50019,#county\:51011,#county\:51033,#county\:51036,#county\:51051,#county\:51053,#county\:51057,#county\:51063,#county\:51071,#county\:51077,#county\:51081,#county\:51083,#county\:51101,#county\:51113,#county\:51135,#county\:51141,#county\:51169,#county\:53009,#county\:53021,#county\:53071,#county\:54001,#county\:54041,#county\:54043,#county\:54067,#county\:54077,#county\:54095,#county\:54103,#county\:55033,#county\:55043,#county\:55081,#county\:55095,#county\:55115,#county\:55121,#county\:55137{fill:#F768A1} +#county\:01001,#county\:01017,#county\:01021,#county\:01031,#county\:01123,#county\:05005,#county\:05009,#county\:05055,#county\:05085,#county\:05091,#county\:05093,#county\:05115,#county\:05145,#county\:06005,#county\:06039,#county\:08101,#county\:12023,#county\:12063,#county\:12121,#county\:12125,#county\:13011,#county\:13017,#county\:13023,#county\:13029,#county\:13039,#county\:13053,#county\:13069,#county\:13075,#county\:13105,#county\:13109,#county\:13111,#county\:13123,#county\:13131,#county\:13145,#county\:13169,#county\:13175,#county\:13177,#county\:13187,#county\:13207,#county\:13231,#county\:13237,#county\:13261,#county\:13279,#county\:13281,#county\:13291,#county\:16005,#county\:16065,#county\:17049,#county\:17073,#county\:17081,#county\:17083,#county\:17109,#county\:17117,#county\:17121,#county\:17127,#county\:17133,#county\:17137,#county\:17141,#county\:17145,#county\:17157,#county\:17165,#county\:17185,#county\:17203,#county\:18015,#county\:18027,#county\:18031,#county\:18047,#county\:18049,#county\:18051,#county\:18055,#county\:18061,#county\:18073,#county\:18075,#county\:18079,#county\:18115,#county\:18119,#county\:18129,#county\:18135,#county\:18137,#county\:18147,#county\:18159,#county\:18165,#county\:18175,#county\:19017,#county\:19045,#county\:19049,#county\:19099,#county\:19111,#county\:19125,#county\:19127,#county\:19181,#county\:19187,#county\:20037,#county\:20061,#county\:20079,#county\:20125,#county\:20155,#county\:21003,#county\:21017,#county\:21043,#county\:21051,#county\:21065,#county\:21079,#county\:21083,#county\:21095,#county\:21097,#county\:21103,#county\:21123,#county\:21137,#county\:21155,#county\:21159,#county\:21161,#county\:21177,#county\:21191,#county\:21197,#county\:21203,#county\:21207,#county\:21213,#county\:21215,#county\:21223,#county\:22007,#county\:22039,#county\:22047,#county\:22099,#county\:22117,#county\:22119,#county\:23015,#county\:24019,#county\:24029,#county\:26007,#county\:26029,#county\:26035,#county\:26047,#county\:26051,#county\:26089,#county\:26105,#county\:26107,#county\:26123,#county\:26157,#county\:26165,#county\:27035,#county\:27041,#county\:27049,#county\:27059,#county\:27067,#county\:27079,#county\:27085,#county\:27099,#county\:27103,#county\:28025,#county\:28027,#county\:28043,#county\:28071,#county\:28083,#county\:28085,#county\:28109,#county\:28115,#county\:28117,#county\:28137,#county\:28145,#county\:29023,#county\:29029,#county\:29069,#county\:29101,#county\:29107,#county\:29109,#county\:29113,#county\:29127,#county\:29159,#county\:29161,#county\:29175,#county\:29209,#county\:29213,#county\:29219,#county\:29225,#county\:31001,#county\:31053,#county\:31119,#county\:32005,#county\:32031,#county\:35049,#county\:35061,#county\:36009,#county\:36017,#county\:36077,#county\:36095,#county\:36097,#county\:36101,#county\:36113,#county\:36115,#county\:36121,#county\:36123,#county\:37009,#county\:37011,#county\:37013,#county\:37033,#county\:37037,#county\:37039,#county\:37047,#county\:37053,#county\:37061,#county\:37079,#county\:37091,#county\:37099,#county\:37113,#county\:37117,#county\:37121,#county\:37123,#county\:37163,#county\:37199,#county\:38017,#county\:39019,#county\:39031,#county\:39047,#county\:39053,#county\:39065,#county\:39069,#county\:39071,#county\:39073,#county\:39105,#county\:39131,#county\:39137,#county\:39161,#county\:39175,#county\:40019,#county\:40021,#county\:40037,#county\:40047,#county\:40071,#county\:40097,#county\:40111,#county\:40115,#county\:40135,#county\:41009,#county\:41029,#county\:41039,#county\:42015,#county\:42031,#county\:42033,#county\:42059,#county\:42061,#county\:42065,#county\:42067,#county\:42115,#county\:42127,#county\:42131,#county\:45001,#county\:45023,#county\:45025,#county\:45027,#county\:45043,#county\:45055,#county\:45067,#county\:45069,#county\:45071,#county\:45087,#county\:47023,#county\:47025,#county\:47033,#county\:47035,#county\:47041,#county\:47045,#county\:47051,#county\:47057,#county\:47079,#county\:47091,#county\:47097,#county\:47099,#county\:47103,#county\:47111,#county\:47115,#county\:47117,#county\:47121,#county\:47123,#county\:47131,#county\:47159,#county\:47169,#county\:47183,#county\:47185,#county\:48001,#county\:48021,#county\:48055,#county\:48063,#county\:48099,#county\:48199,#county\:48203,#county\:48277,#county\:48291,#county\:48343,#county\:48347,#county\:48401,#county\:48449,#county\:48451,#county\:48459,#county\:48467,#county\:48473,#county\:48479,#county\:48497,#county\:48499,#county\:50003,#county\:50011,#county\:50021,#county\:50025,#county\:50027,#county\:51009,#county\:51015,#county\:51023,#county\:51027,#county\:51035,#county\:51043,#county\:51065,#county\:51075,#county\:51105,#county\:51109,#county\:51117,#county\:51127,#county\:51131,#county\:51133,#county\:51143,#county\:51147,#county\:51167,#county\:51171,#county\:51173,#county\:51193,#county\:51197,#county\:53045,#county\:53057,#county\:53077,#county\:54005,#county\:54019,#county\:54035,#county\:54047,#county\:54053,#county\:54059,#county\:54065,#county\:54073,#county\:54097,#county\:54109,#county\:55005,#county\:55017,#county\:55021,#county\:55029,#county\:55045,#county\:55047,#county\:55061,#county\:55093,#county\:55111,#county\:55135{fill:#DD3497} +#county\:01003,#county\:01009,#county\:01033,#county\:01043,#county\:01045,#county\:01049,#county\:01051,#county\:01083,#county\:01113,#county\:01115,#county\:01121,#county\:01127,#county\:04019,#county\:05031,#county\:05033,#county\:05035,#county\:05069,#county\:05125,#county\:06017,#county\:06029,#county\:06031,#county\:06047,#county\:06053,#county\:06057,#county\:06071,#county\:06079,#county\:06107,#county\:06115,#county\:08069,#county\:12007,#county\:12035,#county\:12039,#county\:12055,#county\:12087,#county\:12089,#county\:12107,#county\:12113,#county\:12119,#county\:13031,#county\:13035,#county\:13055,#county\:13071,#county\:13081,#county\:13083,#county\:13085,#county\:13103,#county\:13119,#county\:13143,#county\:13147,#county\:13157,#county\:13171,#county\:13179,#county\:13189,#county\:13195,#county\:13213,#county\:13227,#county\:13275,#county\:13293,#county\:13311,#county\:15007,#county\:15009,#county\:16055,#county\:17001,#county\:17027,#county\:17029,#county\:17055,#county\:17063,#county\:17077,#county\:17095,#county\:17099,#county\:17177,#county\:17183,#county\:17195,#county\:18001,#county\:18009,#county\:18011,#county\:18017,#county\:18021,#county\:18023,#county\:18033,#county\:18037,#county\:18041,#county\:18069,#county\:18071,#county\:18077,#county\:18083,#county\:18087,#county\:18093,#county\:18099,#county\:18103,#county\:18107,#county\:18113,#county\:18133,#county\:18143,#county\:18145,#county\:18149,#county\:18151,#county\:18169,#county\:18179,#county\:18183,#county\:19033,#county\:19057,#county\:19139,#county\:19155,#county\:19179,#county\:19193,#county\:20161,#county\:20169,#county\:21005,#county\:21009,#county\:21013,#county\:21035,#county\:21041,#county\:21047,#county\:21071,#county\:21077,#county\:21081,#county\:21089,#county\:21101,#county\:21107,#county\:21115,#county\:21121,#county\:21125,#county\:21133,#county\:21157,#county\:21163,#county\:21167,#county\:21173,#county\:21179,#county\:21193,#county\:21195,#county\:21199,#county\:21205,#county\:21209,#county\:21211,#county\:21217,#county\:21235,#county\:21239,#county\:22001,#county\:22015,#county\:22057,#county\:22061,#county\:22079,#county\:22093,#county\:22097,#county\:22101,#county\:22109,#county\:22121,#county\:23013,#county\:24011,#county\:24035,#county\:24039,#county\:24047,#county\:25011,#county\:26015,#county\:26023,#county\:26027,#county\:26037,#county\:26057,#county\:26059,#county\:26067,#county\:26073,#county\:26117,#county\:27009,#county\:27013,#county\:27025,#county\:27131,#county\:27145,#county\:27147,#county\:27169,#county\:28001,#county\:28003,#county\:28045,#county\:28067,#county\:28073,#county\:28075,#county\:28089,#county\:28105,#county\:28113,#county\:28149,#county\:28151,#county\:29031,#county\:29037,#county\:29043,#county\:29071,#county\:29145,#county\:29169,#county\:29201,#county\:31043,#county\:31079,#county\:33005,#county\:33019,#county\:36011,#county\:36019,#county\:36021,#county\:36023,#county\:36035,#county\:36037,#county\:36039,#county\:36045,#county\:36051,#county\:36053,#county\:36073,#county\:36099,#county\:36105,#county\:36107,#county\:37019,#county\:37031,#county\:37041,#county\:37055,#county\:37065,#county\:37069,#county\:37077,#county\:37083,#county\:37087,#county\:37093,#county\:37111,#county\:37125,#county\:37145,#county\:37149,#county\:37153,#county\:37161,#county\:37165,#county\:37169,#county\:37175,#county\:37193,#county\:37197,#county\:39011,#county\:39015,#county\:39021,#county\:39027,#county\:39033,#county\:39037,#county\:39039,#county\:39051,#county\:39059,#county\:39075,#county\:39077,#county\:39079,#county\:39083,#county\:39091,#county\:39097,#county\:39107,#county\:39117,#county\:39127,#county\:39129,#county\:39135,#county\:39141,#county\:39147,#county\:39149,#county\:39159,#county\:39167,#county\:39171,#county\:40017,#county\:40031,#county\:40101,#county\:40119,#county\:40125,#county\:40131,#county\:40145,#county\:40147,#county\:41003,#county\:41053,#county\:41071,#county\:42005,#county\:42039,#county\:42063,#county\:42081,#county\:42087,#county\:42099,#county\:42103,#county\:42109,#county\:42111,#county\:42121,#county\:45031,#county\:45033,#county\:45057,#county\:45059,#county\:45073,#county\:45075,#county\:47003,#county\:47013,#county\:47021,#county\:47029,#county\:47031,#county\:47043,#county\:47053,#county\:47059,#county\:47073,#county\:47107,#county\:47119,#county\:47143,#county\:47147,#county\:47155,#county\:47167,#county\:47171,#county\:47173,#county\:47177,#county\:48005,#county\:48007,#county\:48037,#county\:48139,#county\:48181,#county\:48213,#county\:48221,#county\:48231,#county\:48257,#county\:48367,#county\:48381,#county\:48409,#county\:48469,#county\:48471,#county\:49005,#county\:50013,#county\:50023,#county\:51001,#county\:51003,#county\:51031,#county\:51047,#county\:51061,#county\:51079,#county\:51093,#county\:51099,#county\:51103,#county\:51115,#county\:51119,#county\:51137,#county\:51139,#county\:51145,#county\:51155,#county\:51165,#county\:51185,#county\:51191,#county\:51195,#county\:53005,#county\:53015,#county\:53055,#county\:53073,#county\:54045,#county\:54051,#county\:54057,#county\:54091,#county\:54099,#county\:55027,#county\:55073,#county\:55097,#county\:55109,#county\:55141{fill:#AE017E} +#county\:01015,#county\:01055,#county\:01069,#county\:01077,#county\:01081,#county\:01095,#county\:01101,#county\:01103,#county\:01117,#county\:01125,#county\:02020,#county\:05007,#county\:05045,#county\:05051,#county\:05131,#county\:05143,#county\:06007,#county\:06019,#county\:06055,#county\:06061,#county\:06065,#county\:06083,#county\:06097,#county\:06101,#county\:06113,#county\:08035,#county\:08041,#county\:09005,#county\:09015,#county\:10001,#county\:10005,#county\:12001,#county\:12005,#county\:12015,#county\:12017,#county\:12019,#county\:12021,#county\:12053,#county\:12061,#county\:12069,#county\:12083,#county\:12085,#county\:12091,#county\:12097,#county\:12105,#county\:12109,#county\:13009,#county\:13013,#county\:13015,#county\:13045,#county\:13077,#county\:13095,#county\:13115,#county\:13127,#county\:13129,#county\:13137,#county\:13153,#county\:13185,#county\:13217,#county\:13219,#county\:13223,#county\:13225,#county\:13233,#county\:13257,#county\:13277,#county\:13285,#county\:13295,#county\:13297,#county\:13313,#county\:16001,#county\:16027,#county\:17007,#county\:17019,#county\:17037,#county\:17091,#county\:17093,#county\:17113,#county\:17115,#county\:17167,#county\:17179,#county\:17199,#county\:18005,#county\:18019,#county\:18029,#county\:18053,#county\:18059,#county\:18063,#county\:18065,#county\:18067,#county\:18085,#county\:18091,#county\:18109,#county\:18167,#county\:18173,#county\:18177,#county\:19013,#county\:19061,#county\:19103,#county\:19113,#county\:19169,#county\:20045,#county\:20103,#county\:21021,#county\:21029,#county\:21049,#county\:21059,#county\:21073,#county\:21093,#county\:21113,#county\:21145,#county\:21151,#county\:21185,#county\:21227,#county\:22005,#county\:22017,#county\:22019,#county\:22045,#county\:22063,#county\:22073,#county\:22087,#county\:22089,#county\:22095,#county\:22103,#county\:22105,#county\:23001,#county\:23011,#county\:23023,#county\:23031,#county\:24001,#county\:24015,#county\:24017,#county\:24021,#county\:24037,#county\:24041,#county\:24043,#county\:24045,#county\:25003,#county\:25007,#county\:25015,#county\:25019,#county\:26005,#county\:26017,#county\:26021,#county\:26025,#county\:26045,#county\:26055,#county\:26075,#county\:26087,#county\:26091,#county\:26093,#county\:26111,#county\:26115,#county\:26145,#county\:26147,#county\:26149,#county\:26155,#county\:26159,#county\:27019,#county\:27109,#county\:27139,#county\:27141,#county\:27171,#county\:28033,#county\:28035,#county\:28049,#county\:28059,#county\:28081,#county\:28087,#county\:28121,#county\:29019,#county\:29021,#county\:29051,#county\:29097,#county\:29165,#county\:29187,#county\:32003,#county\:33001,#county\:33013,#county\:34019,#county\:34033,#county\:34037,#county\:34041,#county\:35028,#county\:36007,#county\:36013,#county\:36015,#county\:36057,#county\:36065,#county\:36069,#county\:36075,#county\:36083,#county\:36091,#county\:36109,#county\:36111,#county\:36117,#county\:37003,#county\:37023,#county\:37027,#county\:37045,#county\:37049,#county\:37057,#county\:37059,#county\:37085,#county\:37089,#county\:37097,#county\:37101,#county\:37105,#county\:37107,#county\:37109,#county\:37127,#county\:37133,#county\:37139,#county\:37147,#county\:37151,#county\:37155,#county\:37157,#county\:37159,#county\:37167,#county\:37171,#county\:37179,#county\:37181,#county\:37189,#county\:37191,#county\:37195,#county\:39003,#county\:39005,#county\:39007,#county\:39009,#county\:39013,#county\:39029,#county\:39041,#county\:39045,#county\:39055,#county\:39063,#county\:39081,#county\:39087,#county\:39089,#county\:39101,#county\:39109,#county\:39119,#county\:39123,#county\:39139,#county\:39143,#county\:39145,#county\:39157,#county\:39169,#county\:39173,#county\:41005,#county\:41047,#county\:42001,#county\:42013,#county\:42019,#county\:42021,#county\:42025,#county\:42027,#county\:42037,#county\:42051,#county\:42055,#county\:42073,#county\:42085,#county\:42089,#county\:42093,#county\:42097,#county\:42107,#county\:42119,#county\:42125,#county\:45003,#county\:45007,#county\:45013,#county\:45015,#county\:45021,#county\:45035,#county\:45041,#county\:45047,#county\:45051,#county\:45077,#county\:45085,#county\:45091,#county\:46099,#county\:47001,#county\:47009,#county\:47011,#county\:47019,#county\:47089,#county\:47105,#county\:47113,#county\:47125,#county\:47141,#county\:47145,#county\:47149,#county\:47165,#county\:47187,#county\:47189,#county\:48027,#county\:48039,#county\:48041,#county\:48091,#county\:48135,#county\:48187,#county\:48209,#county\:48245,#county\:48251,#county\:48303,#county\:48309,#county\:48329,#county\:48339,#county\:48361,#county\:48375,#county\:48423,#county\:48441,#county\:48485,#county\:48491,#county\:49049,#county\:50007,#county\:51069,#county\:51073,#county\:51085,#county\:51089,#county\:51121,#county\:51149,#county\:51177,#county\:51187,#county\:51800,#county\:53061,#county\:53063,#county\:53067,#county\:54003,#county\:54009,#county\:54033,#county\:54037,#county\:54039,#county\:54049,#county\:54055,#county\:54061,#county\:54079,#county\:54081,#county\:54107,#county\:55015,#county\:55035,#county\:55039,#county\:55055,#county\:55063,#county\:55071,#county\:55087,#county\:55105,#county\:55117,#county\:55127,#county\:55131{fill:#7A0177} +#county\:01073,#county\:01089,#county\:01097,#county\:04013,#county\:05119,#county\:06001,#county\:06013,#county\:06037,#county\:06041,#county\:06059,#county\:06067,#county\:06073,#county\:06075,#county\:06077,#county\:06081,#county\:06085,#county\:06087,#county\:06095,#county\:06099,#county\:06111,#county\:08001,#county\:08005,#county\:08013,#county\:08031,#county\:08059,#county\:09001,#county\:09003,#county\:09007,#county\:09009,#county\:09011,#county\:09013,#county\:10003,#county\:11001,#county\:12009,#county\:12011,#county\:12031,#county\:12033,#county\:12057,#county\:12071,#county\:12073,#county\:12081,#county\:12025,#county\:12095,#county\:12099,#county\:12101,#county\:12103,#county\:12111,#county\:12115,#county\:12117,#county\:12127,#county\:13021,#county\:13047,#county\:13051,#county\:13057,#county\:13059,#county\:13063,#county\:13067,#county\:13073,#county\:13089,#county\:13097,#county\:13113,#county\:13117,#county\:13121,#county\:13135,#county\:13139,#county\:13151,#county\:13215,#county\:13245,#county\:13247,#county\:13255,#county\:15003,#county\:17031,#county\:17043,#county\:17089,#county\:17097,#county\:17111,#county\:17119,#county\:17143,#county\:17161,#county\:17163,#county\:17197,#county\:17201,#county\:18003,#county\:18035,#county\:18039,#county\:18043,#county\:18057,#county\:18081,#county\:18089,#county\:18095,#county\:18097,#county\:18105,#county\:18127,#county\:18141,#county\:18157,#county\:18163,#county\:19153,#county\:19163,#county\:20091,#county\:20173,#county\:20177,#county\:20209,#county\:21015,#county\:21019,#county\:21037,#county\:21067,#county\:21111,#county\:21117,#county\:22033,#county\:22051,#county\:22055,#county\:22071,#county\:23005,#county\:24003,#county\:24005,#county\:24009,#county\:24013,#county\:24025,#county\:24027,#county\:24031,#county\:24033,#county\:25001,#county\:25005,#county\:25009,#county\:25013,#county\:25017,#county\:25021,#county\:25023,#county\:25025,#county\:25027,#county\:26049,#county\:26065,#county\:26077,#county\:26081,#county\:26099,#county\:26121,#county\:26125,#county\:26139,#county\:26161,#county\:26163,#county\:27003,#county\:27037,#county\:27053,#county\:27123,#county\:27163,#county\:28047,#county\:29047,#county\:29077,#county\:29095,#county\:29099,#county\:29183,#county\:29189,#county\:31055,#county\:31109,#county\:31153,#county\:32510,#county\:33011,#county\:33015,#county\:33017,#county\:34001,#county\:34003,#county\:34005,#county\:34007,#county\:34009,#county\:34011,#county\:34013,#county\:34015,#county\:34017,#county\:34021,#county\:34023,#county\:34025,#county\:34027,#county\:34029,#county\:34031,#county\:34035,#county\:34039,#county\:35001,#county\:36001,#county\:36005,#county\:36027,#county\:36029,#county\:36047,#county\:36055,#county\:36059,#county\:36061,#county\:36063,#county\:36067,#county\:36071,#county\:36079,#county\:36081,#county\:36085,#county\:36087,#county\:36093,#county\:36103,#county\:36119,#county\:37001,#county\:37021,#county\:37025,#county\:37035,#county\:37051,#county\:37063,#county\:37067,#county\:37071,#county\:37081,#county\:37119,#county\:37129,#county\:37135,#county\:37183,#county\:39017,#county\:39023,#county\:39025,#county\:39035,#county\:39043,#county\:39049,#county\:39057,#county\:39061,#county\:39085,#county\:39093,#county\:39095,#county\:39099,#county\:39103,#county\:39113,#county\:39133,#county\:39151,#county\:39153,#county\:39155,#county\:39165,#county\:40027,#county\:40109,#county\:40143,#county\:41051,#county\:41067,#county\:42003,#county\:42007,#county\:42011,#county\:42017,#county\:42029,#county\:42041,#county\:42043,#county\:42045,#county\:42049,#county\:42069,#county\:42071,#county\:42075,#county\:42077,#county\:42079,#county\:42091,#county\:42095,#county\:42101,#county\:42129,#county\:42133,#county\:44001,#county\:44003,#county\:44005,#county\:44007,#county\:44009,#county\:45019,#county\:45045,#county\:45063,#county\:45079,#county\:45083,#county\:47037,#county\:47063,#county\:47065,#county\:47093,#county\:47157,#county\:47163,#county\:47179,#county\:48029,#county\:48061,#county\:48085,#county\:48113,#county\:48121,#county\:48141,#county\:48157,#county\:48167,#county\:48183,#county\:48201,#county\:48215,#county\:48355,#county\:48397,#county\:48439,#county\:48453,#county\:49011,#county\:49035,#county\:49057,#county\:51013,#county\:51515,#county\:51041,#county\:51600,#county\:51620,#county\:51087,#county\:51095,#county\:51107,#county\:51153,#county\:51760,#county\:51770,#county\:51179,#county\:51199,#county\:51510,#county\:51520,#county\:51530,#county\:51540,#county\:51550,#county\:51560,#county\:51570,#county\:51580,#county\:51590,#county\:51595,#county\:51610,#county\:51630,#county\:51640,#county\:51650,#county\:51660,#county\:51670,#county\:51678,#county\:51680,#county\:51683,#county\:51685,#county\:51690,#county\:51700,#county\:51710,#county\:51720,#county\:51730,#county\:51735,#county\:51740,#county\:51750,#county\:51775,#county\:51790,#county\:51810,#county\:51820,#county\:51830,#county\:51840,#county\:53011,#county\:53029,#county\:53033,#county\:53035,#county\:53053,#county\:54011,#county\:54029,#county\:54069,#county\:55009,#county\:55025,#county\:55059,#county\:55079,#county\:55089,#county\:55101,#county\:55133,#county\:55139{fill:#49006A} + diff --git a/sproutcore/apps/fp/resources/stylesheets/presentation/result/charts.css b/sproutcore/apps/fp/resources/stylesheets/presentation/result/charts.css new file mode 100644 index 000000000..fd3f4ecc6 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/presentation/result/charts.css @@ -0,0 +1,45 @@ +.chart-view { + font: 11px sans-serif; +} +.chart-view results-chart-title { + font-weight: bold; +} + +.chart-view .axis path, +.chart-view .axis line, +.chart-view .zero-line { + fill: none; + stroke: black; + stroke-width: 1; + shape-rendering: crispEdges; +} + +.chart-view .x-axis path { + display: none; +} + +.chart-view .legend { + float: left; + background-color: blue; /*#fff;*/ + border: 1px solid #bbb; + margin: 1px 1px 1px 1px; +} + +.chart-view .text { + font: 10px sans-serif; + fill: black; + stroke: none; +} + +div.tooltip { + position: absolute; + text-align: center; + width: 60px; + height: 14px; + padding: 2px; + font: 12px sans-serif; + background: lightyellow; + border: 0; + border-radius: 8px; + pointer-events: none; +} \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/stylesheets/presentation/result/legends.css b/sproutcore/apps/fp/resources/stylesheets/presentation/result/legends.css new file mode 100644 index 000000000..b1987eb7c --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/presentation/result/legends.css @@ -0,0 +1,29 @@ +.legend-view { +} + + +.chart-view .axis text { + font: 10px sans-serif; +} + +.chart-view .legend { + float: left; + background-color: #fff; + width: 300px; + height: 6000px; + border: 1px solid #bbb; + margin: 1px 1px 1px 1px; +} + +div.tooltip { + position: absolute; + text-align: center; + width: 60px; + height: 14px; + padding: 2px; + font: 12px sans-serif; + background: lightyellow; + border: 0; + border-radius: 8px; + pointer-events: none; +} \ No newline at end of file diff --git a/sproutcore/apps/fp/resources/stylesheets/styles.css b/sproutcore/apps/fp/resources/stylesheets/styles.css new file mode 100644 index 000000000..c9763df68 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/styles.css @@ -0,0 +1,125 @@ + +/* +.sc-button { + @include box-shadow(inset 0 1px 0 rgba(255,255,255,0.5)); +} +*/ + +body { + -webkit-font-smoothing: antialiased; + text-rendering: optimizelegibility; +} + +/* Fix for Chrome bug on accelerated panes */ +.sc-pane { + -webkit-transform: none; +} + +$theme.sc-text-field-view { + font-size: 14px; + .hint { + font-size: 14px; + line-height: 30px; + } + + .padding { + left: 14px; + } +} + +$theme.toolbar { + .checkbox { + .button { + left: 13px; + top: 50%; + margin-top: -8px; + } + + .label { + left: 47px; + line-height: 36px; + } + } +} + +.sc-list-insertion-point { + overflow: visible; + background-color: #C9D3DF; + z-index: 1000; +} + +.sc-list-insertion-point > .anchor { + position: absolute ; + width: 8px; + top: -3px; + left: -4px; + height: 8px; + border-radius: 4px; + background-color: #C9D3DF; +} + + .loading-image { + -webkit-animation-name: spinner; + -webkit-animation-timing-function: linear; + -webkit-animation-iteration-count: infinite; + -webkit-animation-duration: 6s; + -webkit-transform-style: preserve-3d; + &.hover { + -webkit-animation-play-state: paused; + } + } + +$theme.overlay-view { + background-color: #D1DAE4; +} + +$theme.sc-split-divider-view { + .line { + display: none; + } +} + +$theme.clear-icon { + height: 24px; + width: 24px; + @include slice('../images/clear.png'); +} +$theme.delete-icon { + height: 24px; + width: 24px; + @include slice('../images/delete.png'); +} +$theme.add-icon { + height: 24px; + width: 24px; + @include slice('../images/add.png'); +} +$theme.sc-button-view { + .icon.add-icon { + height: 16px; + width: 16px; + margin-right: 2px; + @include slice('../images/add.png'); + } +} + +$theme.new-record { + font-style:italic; +} + +$theme.dirty-record { + font-style:italic; +} + +$theme.error-progress-overlay-view { + + .content { + position: absolute; + left: 0; + top: 0; + bottom: 0; + + /* width set in rendering */ + @include slice("../images/error_progress_view_content.png", $left: 1, $right: 1); + } +} diff --git a/sproutcore/apps/fp/resources/stylesheets/tests/test.css b/sproutcore/apps/fp/resources/stylesheets/tests/test.css new file mode 100644 index 000000000..74e9ba759 --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/tests/test.css @@ -0,0 +1,3 @@ +$theme.footprint-test-title { + color: #FF0000 +} diff --git a/sproutcore/apps/fp/resources/stylesheets/tool/tool.css b/sproutcore/apps/fp/resources/stylesheets/tool/tool.css new file mode 100644 index 000000000..43cc5ae8e --- /dev/null +++ b/sproutcore/apps/fp/resources/stylesheets/tool/tool.css @@ -0,0 +1,114 @@ +$theme: '.ace.footprint'; + +$theme.featurer-bar { + margin-left:10px; + margin-right:10px; +} + +$theme.theme-button { + + @include border-radius(4px); + + &.theme-button-blue { + border: 1px solid #1B5AAA; + color: white; + @include background-image(linear-gradient(bottom, #4B8ADA 0%, #447EC3 100%)); + @include box-shadow(inset 0 1px 0 #5B9AEA ); + background-color: #589AEA; + + &.active, &.sel { + @include background-image(linear-gradient(bottom, #3BEEEE 0%, #34CCCC 100%)); + @include box-shadow(inset 0 1px 0 #4BAAAA); + background-color: #559ADA; + } + + &.is-full-redevelopment { + @include background-image(linear-gradient(bottom, #EE0000 0%, #AA0000 100%)); + @include box-shadow(inset 0 1px 0 #FF0000 ); + background-color: #BB0000; + + &.active, &.sel { + @include background-image(linear-gradient(bottom, #660000 0%, #880000 100%)); + @include box-shadow(inset 0 1px 0 #DE0000 ); + background-color: #BB0000; + } + } + } + + &.theme-button-gold { + border: 1px solid #AAAA00; + color: white; + @include background-image(linear-gradient(bottom, #AAAA00 0%, #FFFF93 100%)); + @include box-shadow(inset 0 1px 0 #999900 ); + background-color: #888800; + + &.active, &.sel { + @include background-image(linear-gradient(bottom, #EEEE00 0%, #FFFF00 100%)); + @include box-shadow(inset 0 1px 0 #999900 ); + background-color: #888800; + } + } + + &.theme-button-green { + border: 1px solid #44AA00; + color: black; + @include background-image(linear-gradient(bottom, #22AA00 0%, #66EE00 100%)); + @include box-shadow(inset 0 1px 0 #999999 ); + background-color: #228800; + + &.active, &.sel { + @include background-image(linear-gradient(bottom, #44CCCC 0%, #AAEEEE 100%)); + @include box-shadow(inset 0 1px 0 #999999 ); + background-color: #22CC22; + } + } + + &.theme-button-gray { + border: 1px solid #AAAAAA; + color: black; + @include background-image(linear-gradient(bottom, #AAAAAA 0%, #EEEEEE 100%)); + @include box-shadow(inset 0 1px 0 #999999 ); + background-color: #888888; + + &.active, &.sel { + @include background-image(linear-gradient(bottom, #CCCCCC 0%, #EEEEEE 100%)); + @include box-shadow(inset 0 1px 0 #999999 ); + background-color: #CCCCCC; + } + } + + /* Label size. (Specify other button sizes for your app can be specified too, see below.) */ + label.sc-button-label { + line-height: 18px; + padding-left: 0; + padding-right: 0; + height: auto; + } + &.theme-button-shorter { + label.sc-button-label { + line-height: 16px; + /* Layout fix for the dropdown arrow button. */ + margin-top: -1px; + img { + margin-right: 0; + left: 0; + } + } + } + &.theme-button-short { + label.sc-button-label { + line-height: 20px; + } + } + &.theme-button-tall { + label.sc-button-label { + line-height: 28px; + } + } + + /* The inner divs should just go away. */ + .left, .middle, .right { + display: none; + } +} + diff --git a/sproutcore/apps/fp/states/analytic_states/showing_analytic_states.js b/sproutcore/apps/fp/states/analytic_states/showing_analytic_states.js new file mode 100644 index 000000000..c1fd44fb0 --- /dev/null +++ b/sproutcore/apps/fp/states/analytic_states/showing_analytic_states.js @@ -0,0 +1,18 @@ +/*** + * The state that manages the projects pane at the top of the application + * @type {Class} + */ +Footprint.ShowingAnalyticState = SC.State.design({ + + initialSubstate: 'readyState', + readyState: SC.State, + + showFiscalModule: function() { + Footprint.showingAnalyticModuleController.set('nowShowing', 'Footprint.FiscalModuleManagementView'); + }, + + showVMTModule: function() { + Footprint.showingAnalyticModuleController.setPath('nowShowing', 'Footprint.VmtModuleManagementView'); + } + +}); diff --git a/sproutcore/apps/fp/states/application_ready_state.js b/sproutcore/apps/fp/states/application_ready_state.js new file mode 100644 index 000000000..b6b27a97f --- /dev/null +++ b/sproutcore/apps/fp/states/application_ready_state.js @@ -0,0 +1,13 @@ +Footprint.ApplicationReadyState = SC.State.extend({ + + enterState: function() { + // Check for authentication and respond accordingly + this.gotoState('loggingInState'); + }, + + exitState: function() { + // Nothing to worry about here. + } + +}); + diff --git a/sproutcore/apps/fp/states/built_form_states/showing_built_forms_state.js b/sproutcore/apps/fp/states/built_form_states/showing_built_forms_state.js new file mode 100644 index 000000000..1f7a9d1d8 --- /dev/null +++ b/sproutcore/apps/fp/states/built_form_states/showing_built_forms_state.js @@ -0,0 +1,177 @@ +/*** + * The state that manages the projects pane at the top of the application + * @type {Class} + */ +Footprint.ShowingBuiltFormsState = SC.State.design({ + buildingDidChange: function(context) { + // Let this propagate through to other listeners + return NO; + }, + buildingsDidChange: function(context) { + this.gotoState('buildingsAreReadyState', context) + }, + buildingTypeDidChange: function(context) { + // Let this propagate through to other listeners + return NO; + }, + buildingTypesDidChange: function(context) { + this.gotoState('buildingTypesAreReadyState', context) + }, + placetypeDidChange: function(context) { + // Let this propagate through to other listeners + return NO; + }, + placetypesDidChange: function(context) { + this.gotoState('placetypesAreReadyState', context) + }, + + doManageBuiltForms: function(context) { + // Default behavior is to view/manage buildings + var pluralContext = toArrayController(context, {crudType:'view'}); + this.doManageBuildings(pluralContext); + }, + + doManageBuildings: function(context) { + // Tell the modal_state to show the BuiltForm panes, + // even though our data might not be ready yet. + var localContext = { + infoPane: 'Footprint.BuiltFormInfoPane', + nowShowing:'Footprint.ManageBuildingView', + recordType: Footprint.PrimaryComponent, + recordsEditController: Footprint.buildingsEditController, + loadingController:Footprint.buildingsController + }; + var pluralContext = toArrayController(filter_keys(context, ['crudType', 'content']), localContext) + this.gotoState('builtFormEditState', pluralContext); + }, + + doManageBuildingTypes: function(context) { + var localContext = { + infoPane: 'Footprint.BuiltFormInfoPane', + nowShowing:'Footprint.ManageBuildingTypeView', + recordType: Footprint.PlacetypeComponent, + recordsEditController: Footprint.buildingTypesEditController, + loadingController:Footprint.buildingTypesController + }; + var pluralContext = toArrayController(filter_keys(context, ['crudType', 'content']), localContext) + this.gotoState('builtFormEditState', pluralContext); + }, + + doManagePlaceTypes: function(context) { + var localContext = { + infoPane: 'Footprint.BuiltFormInfoPane', + nowShowing:'Footprint.ManagePlacetypeView', + recordType: Footprint.Placetype, + recordsEditController: Footprint.placetypesEditController, + loadingController:Footprint.placetypesController + }; + var pluralContext = toArrayController(filter_keys(context, ['crudType', 'content']), localContext) + this.gotoState('builtFormEditState', pluralContext); + }, + + initialSubstate: 'readyState', + readyState: SC.State, + builtFormEditState: SC.State.extend({ + + initialSubstate: 'loadingState', + loadingState: Footprint.LoadingState.extend({ + didLoadEvent: 'didLoadBuiltForms', + didFailEvent: 'didFailToLoadBuiltForms', + + enterState: function(context) { + // Show the modal while the records are loading so that the user knows something is happening. + Footprint.statechart.sendAction('doHideModal', context); + Footprint.statechart.sendAction('doShowModal', context); + + this.set('loadingController', context.get('loadingController')) ; + sc_super(); + }, + + recordArray: function() { + if (this.getPath('loadingController.status') === SC.Record.READY_CLEAN) + return this.getPath('loadingController.content'); + return Footprint.store.find(SC.Query.create({ + recordType: this._context.get('recordType'), + location: SC.Query.REMOTE + })); + }, + + didLoadBuiltForms: function(context) { + this.gotoState('builtFormsAreReadyState', this._context); + }, + didFailToLoadBuiltForms: function() { + this.gotoState(this.getPath('parentState.parentState.fullName')); + } + }), + + builtFormsAreReadyState: Footprint.RecordsAreReadyState.extend({ + recordsDidUpdateEvent: 'builtFormsDidChange', + recordsDidFailToUpdateEvent: 'builtFormsDidFailToUpdate', + updateAction: 'doBuiltFormUpdate', + undoAction: 'doBuiltFormUndo', + + enterState: function(context) { + + this._nestedStore = Footprint.store.chainAutonomousStore(); + this._content = this._nestedStore.find(SC.Query.local( + context.get('recordType'), { + conditions: '{storeKeys} CONTAINS storeKey', + storeKeys:context.get('loadingController').mapProperty('storeKey') + })); + this._context = SC.ArrayController.create({content: this._content, recordType:context.get('recordType')}); + sc_super(); + + var crudContext = toArrayController(context, {content:context.getPath('recordsEditController.selection.firstObject')}) + + // Call doCloneRecord, doCreateRecord + Footprint.statechart.sendAction('do%@Record'.fmt(context.get('crudType').capitalize()), crudContext) + } + }) + }), + + loadManagementState: SC.State.extend({ + +// doVisualize: function() { +// Footprint.statechart.sendAction('doViewRecord', { +// infoPane: 'Footprint.VisualizePane' +// }) +// }, + + initialSubstate: 'initialLoadState', + + initialLoadState: SC.State.extend({ + enterState: function(context){ + Footprint.statechart.sendEvent('builtFormDidChange', SC.Object.create({content : Footprint.builtFormCategoriesTreeController.getPath('selection.firstObject')})) + } + }), + // LoadingState when the Footprint.builtFormActiveController is not ready + loadingFlatBuiltFormState: Footprint.LoadingState.extend({ + + recordType:Footprint.FlatBuiltForm, + didLoadEvent: 'didLoadFlatBuiltForm', + loadingController: Footprint.flatBuiltFormsController, + recordArray:function() { + return Footprint.store.find( + SC.Query.create({ + recordType: this.get('recordType'), + location:SC.Query.REMOTE, + parameters:{ + built_form_id: this._context.getPath('content.id') + } + }) + ); + }, + didLoadFlatBuiltForm:function() { + // Get out of this state + this.gotoState('loadedState'); + } + }), + loadedState: SC.State, + + builtFormDidChange: function(context) { + this.gotoState('loadingFlatBuiltFormState', context); + } + + }) +}); + diff --git a/sproutcore/apps/fp/states/layer_states/showing_layers_state.js b/sproutcore/apps/fp/states/layer_states/showing_layers_state.js new file mode 100644 index 000000000..bd16e786e --- /dev/null +++ b/sproutcore/apps/fp/states/layer_states/showing_layers_state.js @@ -0,0 +1,193 @@ +sc_require('states/loading_scenario_dependencies_states'); + +/*** + * The state that manages the projects pane at the top of the application + * @type {Class} + */ +Footprint.ShowingLayersState = SC.State.design({ + + layerDidChange: function(context) { + // Let this propagate through to other listeners + return NO; + }, + layersDidChange: function(context) { + this.gotoState('layersAreReadyState', context) + }, + + /*** + * Sets the value property of the view to hidden if visible or visible if hidden + * The value property is bound to some kind of visibility property of a model instance. + * For instance visibilityView of LayerLibraryView has its value bound to '.parentView*content.visibility' + * @param view + */ + visibleAction: function(view) { + Footprint.layerLibraryActiveController.set('solos', []); + if (view.get('value') == Footprint.VISIBLE) { + // hide if visible + view.set('value', Footprint.HIDDEN); + } + else { + // show if hidden + view.set('value', Footprint.VISIBLE); + } + }, + + initialSubstate: 'readyState', + + readyState: SC.State.extend({ + enterState: function() { + // If already ready proceed, otherwise hang out here. + if ([SC.Record.READY_CLEAN, SC.Record.READY_DIRTY].contains(Footprint.layersController.get('status'))) { + this.gotoState('layersAreReadyState', Footprint.scenariosController); + } + }, + + layerDidChange: function(context) { + // Let the showing_map_state react too + return NO; + }, + + doExportRecord: function(context) { + if (context.get('content')) { + var record = context.get('content'); + + if (record.kindOf(Footprint.PresentationMedium)) { + var layer_id = context.get('content').get('id'); + var api_key = Footprint.userController.getPath('content.firstObject.api_key'); + var export_request = SC.Request.getUrl("/footprint/%@/export_layer/%@/".fmt(api_key, layer_id)); + export_request.send(); + alert("Exporting the currently selected layer - it will start downloading as soon as it is ready. \n\n " + + "Please do not close your UrbanFootprint session."); + return YES; + } + return NO; + } + }, + + layersAreReadyState: Footprint.RecordsAreReadyState.extend({ + recordsDidUpdateEvent: 'layersDidUpdate', + recordsDidFailToUpdateEvent: 'layersDidFailToUpdate', + updateAction: 'doLayerUpdate', + undoAction: 'doLayerUndo', + undoAttributes: ['name', 'year', 'description'], + crudParams: function() { + // We need an existing layer to use as a template (for now) + var template = Footprint.layersEditController.getPath('selection.firstObject'); + if (!template || template.get('id') < 0) + template = Footprint.layersEditController.filter(function(layer) { return layer.get('id') > 0 })[0]; + return { + infoPane: 'Footprint.LayerInfoPane', + recordType: Footprint.Layer, + recordsEditController: Footprint.layersEditController, + content:template + }; + }.property().cacheable(), + + doCreateLayer: function() { + Footprint.statechart.sendAction('doCreateRecord', this.get('crudParams')); + }, + doCloneLayer: function() { + Footprint.statechart.sendAction('doCloneRecord', this.get('crudParams')); + }, + doCreateLayerFromSelection: function() { + Footprint.statechart.sendAction('doCloneRecord', this.get('crudParams')); + }, + doUpdateLayer: function() { + Footprint.statechart.sendAction('doUpdateRecord', this.get('crudParams')); + }, + doViewLayer: function() { + Footprint.statechart.sendAction('doViewRecord', this.get('crudParams')); + }, + + // Layers are always created with a dbEntity, which is the more fundamental + // record to the server. So we listen for socketIO updates about the dbEntity + // save and post-save process + postSaveDbEntityPublisherCompleted: function(context) { + var combinedContext = toArrayController(context, { + keyPath:'db_entity_key', + postProcessingDidEnd: function(context) { + var parentStore = context.getPath('content.store.parentStore'); + var store = context.getPath('content.store'); + var records = Footprint.store.find(SC.Query.local( + Footprint.Scenario, { + conditions: 'id = {id}', + id: context.getPath('content.presentation.config_entity.id') + } + )); + store.get('parentStore').refreshRecords( + records.mapProperty('constructor'), + records.mapProperty('id'), + records.mapProperty('storeKey'), + function() { + // Sync the store to the updated parent store + store.reset(); + } + ); + records = [context.get('content')]; + store.get('parentStore').refreshRecords( + records.mapProperty('constructor'), + records.mapProperty('id'), + records.mapProperty('storeKey') + ); + }}); + Footprint.statechart.sendAction('doUpdateSaveProgress', combinedContext); + }, + + postSaveDbEntityPublisherFailed: function(context) { + // The context (nee the socket event) gives us enough information to + // get the DbEntityInterest. From there, we can query the corresponding + // failed layer. + if (context.get('class_name') !== 'DbEntityInterest') return NO; + var failedRecord = F.store.find(F.DbEntityInterest, context.get('id')), + failedLayer = F.store.find(SC.Query.local(F.Layer, { + conditions: 'db_entity_interest = %@', + parameters: [failedRecord] + })).firstObject(); + if (!failedLayer) return NO; + var failedLayerName = failedLayer.get('name'), + layerLibrary = failedLayer.get('presentation'), + layerLibraryStatus = layerLibrary.get('status'); + // Delete the failed layer, and remove it from its LayerLibrary. + failedRecord.destroy(); + failedLayer.destroy(); + layerLibrary.get('layers').removeObject(failedLayer); + F.store.writeStatus(layerLibrary.get('storeKey'), layerLibraryStatus); + // Show an alert. + SC.AlertPane.warn({ + message: 'Layer Creation Failed', + description: 'There was an error processing "%@". Please try again, and if this continues, please report to your system administrator.'.loc(failedLayerName) + }); + return NO; + }, + + enterState: function(context) { + this._nestedStore = Footprint.store.chainAutonomousStore(); + this._content = this._nestedStore.find(SC.Query.local( + Footprint.Layer, { + conditions: '{storeKeys} CONTAINS storeKey', + storeKeys:context.mapProperty('storeKey') + })).toArray(); + this._context = SC.ArrayController.create({content: this._content, recordType:context.get('recordType')}); + Footprint.layersEditController.set('content', this._context.get('content')); + sc_super(); + }, + + /*** + * + * The undoManager property on each layer + */ + undoManagerProperty: 'undoManager', + + updateContext: function(context) { + var recordCntext = SC.ObjectController.create(); + return this.createModifyContext(recordContext, context) + } + }) + }), + + errorState: SC.State.extend({ + enterState: function() { + + } + }) +}); diff --git a/sproutcore/apps/fp/states/loading_config_entity_states.js b/sproutcore/apps/fp/states/loading_config_entity_states.js new file mode 100644 index 000000000..3f633bb14 --- /dev/null +++ b/sproutcore/apps/fp/states/loading_config_entity_states.js @@ -0,0 +1,160 @@ +sc_require('controllers/global_config_controllers'); +sc_require('controllers/regions_controllers'); +sc_require('controllers/projects_controllers'); +sc_require('controllers/scenarios/scenario_controllers'); + +Footprint.LoadingConfigEntityState = Footprint.LoadingState.extend({ + + parentController:null, + /** + * Queries for all the scenarios of the ConfigEntity + * @returns {*} + */ + recordArray:function() { + return Footprint.store.find( + SC.Query.create({ + recordType: this.get('recordType'), + location:SC.Query.REMOTE, + parameters:{ + //parent_config_entity: this.getPath('parentController.selection.firstObject') + } + })); + } +}); + +Footprint.LoadingGlobalConfigState = Footprint.LoadingState.extend({ + recordType:Footprint.GlobalConfig, + loadingController:Footprint.globalConfigController, + didLoadEvent:'didLoadGlobalConfigController', + + /** + * Queries for the singleton Globalconfig + */ + recordArray:function() { + return Footprint.store.find(SC.Query.remote( + this.get('recordType') + )); + } +}); + +Footprint.LoadingRegionsState = Footprint.LoadingConfigEntityState.design({ + recordType:Footprint.Region, + loadingController: Footprint.regionsController, + parentController:Footprint.globalConfigController, + didLoadEvent:'didLoadRegionController' +}); + +Footprint.LoadingProjectsState = Footprint.LoadingConfigEntityState.design({ + recordType:Footprint.Project, + substatesAreConcurrent: YES, + initialSubstate: null, + loadingController: SC.ArrayController.create(), + parentController: Footprint.regionActiveController, + didLoadEvent:'didLoadProjectController' +}); + +Footprint.LoadingConcurrentDependenciesState = SC.State.extend({ + substatesAreConcurrent: YES, + enterState: function() { + this._loadingControllerQueue = []; + this.get('substates').forEach(function(substate) { + substate.get('loadingController').addObserver('status', this, 'loadingControllerStatusDidChange') + this._loadingControllerQueue.push(substate); + }, this); + }, + + loadingControllerStatusDidChange:function(sender) { + if (sender.get('status') & SC.Record.READY) { + sender.removeObserver('status', this, 'loadingControllerStatusDidChange'); + this._loadingControllerQueue.pop(sender); + } + if (this._loadingControllerQueue.length == 0) { + this.statechart.sendEvent('didLoadConcurrentDependencies'); + } + } +}); + +Footprint.LoadingScenarioDependenciesState = Footprint.LoadingConcurrentDependenciesState.extend({ + didLoadConcurrentDependencies: function() { + this.statechart.sendEvent('didLoadScenarioDependencies'); + }, + loadingScenarioCategoriesState:SC.State.plugin('Footprint.LoadingScenarioCategoriesState'), + loadingBuiltFormTagsState:SC.State.plugin('Footprint.LoadingBuiltFormTagsState'), + loadingLayerTagsState:SC.State.plugin('Footprint.LoadingLayerTagsState'), + //loadingPresentationsState:SC.State.plugin('Footprint.LoadingPresentationsState'), + //loadingBuiltFormSetsState:SC.State.plugin('Footprint.LoadingBuiltFormSetsState'), + loadingPolicySetsState:SC.State.plugin('Footprint.LoadingPolicySetsState') +}); + +Footprint.LoadingScenariosState = Footprint.LoadingConfigEntityState.design({ + recordType:Footprint.Scenario, + loadingController: SC.ArrayController.create(), + parentController: Footprint.projectActiveController, + didLoadEvent:'didLoadScenarioController' +}); + +/*** + * The default post-login state, whether for an authenticated session or an anonymous demo session + * @type {Class} + * TODO rename states to have State suffix + */ +Footprint.LoadingAppState = SC.State.design({ + + // These states are loaded sequentially + loadingGlobalConfigState: Footprint.LoadingGlobalConfigState, + loadingRegionsState: Footprint.LoadingRegionsState, + loadingProjectsState: Footprint.LoadingProjectsState, + loadingScenariosState: Footprint.LoadingScenariosState, + loadingScenarioDependenciesState: Footprint.LoadingScenarioDependenciesState, + + initialSubstate:'loadingGlobalConfigState', + + enterState: function() { + Footprint.mainPage.get('loadingPane').append(); + }, + + didLoadGlobalConfigController: function() { + this.gotoState('loadingRegionsState'); + }, + didLoadRegionController: function() { + var regionController = this.get('loadingController'); + this.getPath('loadingRegionsState.loadingController').forEach(function(region) { + var delegate = Footprint.regionActiveController.get('configEntityDelegate'); + var loadingRegionStateClass = delegate.get('loadingRegionState') + + // TODO for some reason loadingProjectsState doesn't run if this is null + if (loadingRegionStateClass) { + // By adding a substate to a concurrent state that supports concurrent classes, this will + // run when we enter the state, along with the loadingProjectState itself + this.get('loadingProjectsState').addSubstate('loading%@State'.fmt(region.get('key').classify()), loadingRegionStateClass); + } + }, this); + + this.gotoState('loadingProjectsState'); + }, + didLoadProjectController: function() { + this.gotoState('loadingScenarioDependenciesState'); + }, + didLoadScenarioDependencies: function() { + this.gotoState('loadingScenariosState'); + }, + didLoadScenarioController: function(context) { + // Goto without a sending the context. The receiver expect a single Scenario, not the list + this.gotoState('showingAppState') + }, + + didFailLoadingController: function(context) { + SC.AlertPane.error({ + message: 'Login Error', + description: "There was an error logging you in. Please alert the system's administrator. Sorry for the inconvenience!", + buttons: [{ + title: 'OK', + action: 'doLogout' + }] + }) + }, + + exitState:function() { + Footprint.getPath('mainPage.loadingPane').remove(); + } +}); diff --git a/sproutcore/apps/fp/states/loading_scenario_dependencies_states.js b/sproutcore/apps/fp/states/loading_scenario_dependencies_states.js new file mode 100644 index 000000000..d1b3c7b93 --- /dev/null +++ b/sproutcore/apps/fp/states/loading_scenario_dependencies_states.js @@ -0,0 +1,79 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.LoadingScenarioDependencyState = Footprint.LoadingState.extend({ + + loadingStatusValue:1, + + parameters: {}, + eventKey:null, + recordArray:function() { + return Footprint.store.find(SC.Query.create({ + recordType: this.get('recordType'), + location: SC.Query.REMOTE, + parameters:this.get('parameters') + })); + }, + didLoadEvent:function() { + return '%@IsReady'.fmt(this.get('eventKey')); + }.property('loadingController', 'eventKey').cacheable(), + + + didFailEvent: function() { + return '%@DidFail'.fmt(this.get('eventKey')); + }.property('loadingController', 'eventKey').cacheable() +}); + +Footprint.LoadingScenarioCategoriesState = Footprint.LoadingScenarioDependencyState.extend({ + recordType:Footprint.Category, + parameters: { + key: 'category' + }, + loadingController: Footprint.scenarioCategoriesController, + eventKey:'scenarioCategoriesController' +}); + +Footprint.LoadingBuiltFormTagsState = Footprint.LoadingScenarioDependencyState.extend({ + recordType:Footprint.Tag, + loadingController: Footprint.builtFormTagsController, + eventKey:'builtFormTagsController' +}); +Footprint.LoadingLayerTagsState = Footprint.LoadingScenarioDependencyState.extend({ + recordType:Footprint.Tag, + loadingController: Footprint.layerTagsController, + eventKey:'layerTagsController' +}); + +/* +Footprint.LoadingPresentationsState = Footprint.LoadingScenarioDependencyState.extend({ + recordType:Footprint.Presentation, + // Use a private controller to load presentations. The public controllers split them between LayerLibrary + // and ResultLibrary presentations + loadingController: SC.ArrayController.create(), + eventKey:'presentationsController' +}); + +Footprint.LoadingBuiltFormSetsState = Footprint.LoadingScenarioDependencyState.extend({ + recordType:Footprint.BuiltFormSet, + loadingController: Footprint.builtFormSetsController, + eventKey:'builtFormSetsController' +}); +*/ + +Footprint.LoadingPolicySetsState = Footprint.LoadingScenarioDependencyState.extend({ + recordType:Footprint.PolicySet, + loadingController: Footprint.policySetsController, + eventKey:'builtFormSetsController' +}); diff --git a/sproutcore/apps/fp/states/logging_in_state.js b/sproutcore/apps/fp/states/logging_in_state.js new file mode 100644 index 000000000..7bd0bf1af --- /dev/null +++ b/sproutcore/apps/fp/states/logging_in_state.js @@ -0,0 +1,96 @@ + +Footprint.LoggingInState = SC.State.extend({ + + /*** + * Transitions from logging in to loading the application + */ + didCompleteLogin: function() { + this.gotoState('loadingAppState'); + }, + + enterState: function() { + var cookie = Footprint.userController.findCookie(); + if (cookie && cookie.get('value')) { + Footprint.loginController.set('api_key', cookie.get('value')); + this.gotoState('loadingUserState'); + return; + } + Footprint.loginPage.get('mainPane').append(); + }, + + exitState: function() { + Footprint.getPath('loginPage.mainPane').remove(); + Footprint.loginController.set('username', null); + Footprint.loginController.set('password', null); + Footprint.loginController.set('api_key', null); + }, + + doAuthenticate: function() { + if (Footprint.loginController.get('username') && Footprint.loginController.get('password')) { + this.gotoState('loadingUserState') + } + }, + + initialSubstate: 'loginReadyState', + loginReadyState: SC.State, + + loadingUserState: Footprint.LoadingState.extend({ + + loadingController:Footprint.userController, + didLoadEvent:'didLoadUser', + didFailEvent:'didFailToLoadUser', + + enterState: function() { + Footprint.loginPage.get('loginBypassPane').append(); + sc_super(); + }, + + /** + * Queries for all the scenarios of the ConfigEntity + * @returns {*} + */ + recordArray:function() { + var params = {}, + username = Footprint.loginController.get('username'), + password = Footprint.loginController.get('password'), + apiKey = Footprint.loginController.get('api_key'); + if (username && password) { + params.username = username; + params.password = password; + } + if (apiKey) params.api_key = apiKey; + return Footprint.store.find(SC.Query.create({ + recordType: Footprint.User, + location:SC.Query.REMOTE, + parameters: params + })); + }, + + exitState: function() { + Footprint.getPath('loginPage.loginBypassPane').remove(); + sc_super(); + } + }), + + didLoadUser: function(){ + if (Footprint.userController.get('status') & Footprint.Record.READY) { + if (Footprint.userController.get('length') == 1) { + // Put the user apiKey in the cache + Footprint.userController.setCookie(24*60*60*1000); + // All good, go to the loading page + Footprint.statechart.sendEvent("didCompleteLogin"); + } else { + SC.AlertPane.error("A login error occurred"); + Footprint.userController.set('content', null); + Footprint.loginController.set('password', null); + this.gotoState('loginReadyState'); + } + } + }, + + didFailToLoadUser: function() { + SC.AlertPane.error("Authentication Failed"); + this.gotoState('loginReadyState'); + } +}); + diff --git a/sproutcore/apps/fp/states/map_states/showing_map_state.js b/sproutcore/apps/fp/states/map_states/showing_map_state.js new file mode 100644 index 000000000..726a6cc0c --- /dev/null +++ b/sproutcore/apps/fp/states/map_states/showing_map_state.js @@ -0,0 +1,136 @@ +/*** + * The state that manages the map panel portion of the application + * @type {Class} + */ +Footprint.ShowingMapState = SC.State.design({ + + /*** + * Fired when a Scenario becomes active and the layers are ready + * @param context + */ + layersDidChange: function(context) { + if (Footprint.mapController.get('isReady')) { + SC.Timer.schedule({target: this, action: "doProcessLayers", interval: 1000}) + } + return NO; + }, + + doProcessLayers: function() { + Footprint.mapLayerGroupsController.layersWillChange(); + Footprint.mapLayerGroupsController.updateMapLayerGroups(); + Footprint.mapLayerGroupsController.mapLayerGroupsDidUpdate(); + this.invokeNext(function() { + // Once everything is ready, set layer visibility + Footprint.mapController.set('mapLayersNeedZoomUpdate', YES); + }) + }, + + /*** + * Fired when a Layer becomes active + * @param context + */ + layerDidChange: function(context) { + // Re-enter the selectionHandlerState whenever the active layer changes + // This will make sure our layerSelection/features loading happens + // Clear the previous layer's selections + Footprint.layerSelectionsController.set('content', null); + // Make sure layerSelectionActiveController is cleared + this.invokeLast(function() { + this.gotoState('layerSelectionEditState', context); + }); + return NO; + }, + + cancelSelection: function(layerContext) { + // Start over the selectionHandlerState + this.gotoState('layerSelectionEditState', layerContext); + }, + + /*** + * Activates the pencil (select individual features) tool + * @param view + */ + paintPoint: function(view) { + // Prevent selection from beginning before the layerSelectionActiveController is ready + // TODO this should be done instead by disabling the selection buttons + if (Footprint.layerSelectionActiveController.get('status') & SC.Record.READY) { + Footprint.mapToolsController.set('activeMapToolKey', 'pointbrush'); + } + }, + + /*** + * Activate the select (draw a shape to select) tool + * @param view + */ + paintBox: function(view) { + // Prevent selection from beginning before the layerSelectionActiveController is ready + // TODO this should be done instead by disabling the selection buttons + if (Footprint.layerSelectionActiveController.get('status') & SC.Record.READY) { + Footprint.mapToolsController.set('activeMapToolKey', 'rectanglebrush'); + } + }, + + /*** + * Activate the select (draw a shape to select) tool + * @param view + */ + paintPolygon: function(view) { + // Prevent selection from beginning before the layerSelectionActiveController is ready + // TODO this should be done instead by disabling the selection buttons + if (Footprint.layerSelectionActiveController.get('status') & SC.Record.READY) { + Footprint.mapToolsController.set('activeMapToolKey', 'polybrush'); + } + }, + + navigate: function(view) { + Footprint.mapToolsController.set('activeMapToolKey', null); + }, + + /*** + * Undo last paint operation + * @param view + */ + doPaintUndo: function(view) { + Footprint.statechart.sendAction('doFeaturesUndo'); + }, + + /*** + * Redo last undid operation + * @param view + */ + doPaintRedo: function(view) { + Footprint.statechart.sendAction('doFeaturesRedo'); + }, + + /*** + * TODO unimplemented. Revert to the first state in the undo buffer. + * @param view + */ + doPaintRevert: function(view) { + + }, + + /*** + * Responds to zoomToProjectExtent by calling resetExtentToProject on the mapController + * @param view + */ + zoomToProjectExtent: function(view) { + Footprint.mapController.resetExtentToProject(); + }, + + zoomToSelectionExtent: function(view) { + Footprint.mapController.resetExtentToSelection(); + }, + + substatesAreConcurrent: YES, + + // The entry to showingMapState. This is probably a good time to tell the mapController that it's ready + // to show the maps + enterState: function() { + Footprint.mapController.get('readyToCreateMap', YES); + }, + + // All the substates for handling feature selection and updating the selection on the server. This state + // is always active and ready to accept a new selection drawn or queried by the user (unless the active layerSelection is loading). + layerSelectionEditState: SC.State.plugin('Footprint.LayerSelectionEditState') +}); diff --git a/sproutcore/apps/fp/states/project_states/showing_projects_state.js b/sproutcore/apps/fp/states/project_states/showing_projects_state.js new file mode 100644 index 000000000..c89812a41 --- /dev/null +++ b/sproutcore/apps/fp/states/project_states/showing_projects_state.js @@ -0,0 +1,6 @@ +/*** + * The state that manages the projects pane at the top of the application + * @type {Class} + */ +Footprint.ShowingProjectsState = SC.State.design({ +}); diff --git a/sproutcore/apps/fp/states/result_states/showing_results_state.js b/sproutcore/apps/fp/states/result_states/showing_results_state.js new file mode 100644 index 000000000..829bf5461 --- /dev/null +++ b/sproutcore/apps/fp/states/result_states/showing_results_state.js @@ -0,0 +1,25 @@ +/*** + * The state that manages the projects pane at the top of the application + * @type {Class} + */ +sc_require('states/loading_scenario_dependencies_states'); + +Footprint.ShowingResultsState = SC.State.design({ + + initialSubstate:'readyState', + + readyState: SC.State, + + doViewScenario: function(context) { + Footprint.statechart.sendAction('doViewResults', SC.Object.create({ + content:Footprint.scenarioActiveController + })); + return NO + }, + + errorState: SC.State.extend({ + enterState: function() { + + } + }) +}); diff --git a/sproutcore/apps/fp/states/scenario_states/showing_scenarios_state.js b/sproutcore/apps/fp/states/scenario_states/showing_scenarios_state.js new file mode 100644 index 000000000..096b73376 --- /dev/null +++ b/sproutcore/apps/fp/states/scenario_states/showing_scenarios_state.js @@ -0,0 +1,150 @@ +/*** + * The state that manages the projects pane at the top of the application + * @type {Class} + */ + +Footprint.ShowingScenariosState = SC.State.extend({ + + scenarioDidChange: function(context) { + // Let this propagate through to other listeners + return NO; + }, + scenariosDidChange: function(context) { + this.gotoState('scenariosAreReadyState', context) + }, + + /*** + * Export the Scenario record in the context. + * @param context - context.activeRecord contains the Scenario to export + * @returns {*} YES if the context contains a Scenario, else NO + */ + doExportRecord: function(context) { + if (context.get('activeRecord')) { + if (context.get('activeRecord').kindOf(Footprint.Scenario)) { + // TODO export something! + return YES; + }} + return NO; + }, + /*** + * Called when the server-side analytic modules complete, instructing the ResultLibrary instance + * and the mapController to refresh + * @param context + */ + analysisDidComplete: function(context) { + + var scenarios = Footprint.scenariosController.filter(function(scenario) { + return scenario.get('id')==context.get('config_entity_id'); + }); + + scenarios.forEach(function(scenario) { + scenario.getPath('presentations.results').forEach(function(resultLibrary) { + resultLibrary.refresh(); + (resultLibrary.get('results') || []).forEach(function(result) { + SC.RunLoop.begin(); + result.refresh(); + SC.RunLoop.end(); + }); + }, this); + }, this); + // It works better to do this here than in the FeatureUpdatingState. + // Polymaps gets angry if we do it there + Footprint.mapController.refreshLayer(); + }, + + initialSubstate: 'readyState', + readyState: SC.State.extend({ + enterState: function() { + if ([SC.Record.READY_CLEAN, SC.Record.READY_DIRTY].contains(Footprint.scenariosController.get('status'))) { + this.gotoState('scenariosAreReadyState', Footprint.scenariosController); + } + } + }), + + scenariosAreReadyState: Footprint.RecordsAreReadyState.extend({ + recordsDidUpdateEvent: 'scenariosDidChange', + recordsDidFailToUpdateEvent: 'scenariosDidFailToUpdate', + updateAction: 'doScenarioUpdate', + undoAction: 'doScenarioUndo', + undoAttributes: ['name', 'year', 'description'], + + crudParams: function() { + return { + infoPane: 'Footprint.ScenarioInfoPane', + recordsEditController: Footprint.scenariosEditController, + recordType: Footprint.Scenario + }; + }.property().cacheable(), + + doManageScenarios: function() { + this.doViewScenario(); + }, + doCreateScenario: function() { + Footprint.statechart.sendAction('doCreateRecord', this.get('crudParams')); + }, + doCloneScenario: function() { + Footprint.statechart.sendAction('doCloneRecord', this.get('crudParams')); + }, + doEditScenario: function() { + Footprint.statechart.sendAction('doUpdateRecord', this.get('crudParams')); + }, + doViewScenario: function() { + Footprint.statechart.sendAction('doViewRecord', this.get('crudParams')); + }, + + editScenarioDidChange: function(context) { + // Send the generic action. The modal_crud_state will respond and + // check that the context equals the recordsEditController + Footprint.statechart.sendAction('selectedEditRecordDidChange', context); + }, + + // Saving update events + // Each update sends a 'proportion' value. When this proportion hits 100% the save is + // completed. We use proportion both to show status and because concurrent publishers + // on the server make it impossible to know otherwise when everything is complete. + postSaveConfigEntityPublisherCompleted: function(context) { + if (!['Scenario', 'BaseScenario', 'FutureScenario'].contains(context.get('config_entity_class_name'))) + // We only care about Scenarios in this state + return NO; + + var combinedContext = toArrayController(context, { + postProcessingDidEnd: function(context) { + var parentStore = context.getPath('content.store.parentStore'); + [context.get('content')].forEach(function(record) { + record.refresh(); + }) + }}); + Footprint.statechart.sendAction('doUpdateSaveProgress', combinedContext) + }, + postSaveConfigEntityPublisherFailed: function(context) { + if (!['Scenario', 'BaseScenario', 'FutureScenario'].contains(context.get('config_entity_class_name'))) + // We only care about Scenarios in this state + return NO; + Footprint.statechart.sendAction('postProcessRecordsDidFail', context) + }, + + + undoManagerProperty: 'undoManager', + + /*** + * Scenarios have a basic update context. No flags are passed in for bulk updates + * @returns {*} + */ + updateContext: function(context) { + var recordContext = SC.ObjectController.create(); + return this.createModifyContext(recordContext, context); + }, + + enterState: function(context) { + // Do nothing here for now. Don't call the parent method + } + }), + + /*** + * Called by socketIO when asynchronous creation of the instance's components completes + */ + scenarioCreationDidComplete: function(context) { + // Refresh the Scenario ArrayController to sync the new record + Footprint.scenariosController.get('content').refresh(); + } +}); diff --git a/sproutcore/apps/fp/states/selection_states/features_are_ready_state.js b/sproutcore/apps/fp/states/selection_states/features_are_ready_state.js new file mode 100644 index 000000000..05a028e6c --- /dev/null +++ b/sproutcore/apps/fp/states/selection_states/features_are_ready_state.js @@ -0,0 +1,125 @@ +/*** + * Updates features + * @type {*|RangeObserver|Class|void} + */ +Footprint.FeaturesAreReadyState = Footprint.RecordsAreReadyState.extend({ + + recordsDidUpdateEvent: 'featuresDidUpdate', + recordsDidFailToUpdateEvent: 'featuresDidFailToUpdate', + updateAction: 'doFeaturesUpdate', + undoAction: 'doFeaturesUndo', + // TODO these are just the attributes we update when painting. + // Obviously if editing full features is enabled, more attributes have to be undoable + // In that case its probably easiest to know all attributes and try to undo all of them. + undoAttributes: ['built_form', 'dev_pct', 'density_pct', 'total_redev'], + /*** + * + * The undoManager for the features of the current layerSelection + */ + undoManagerController: Footprint.layerSelectionActiveController, + undoManagerProperty: 'featureUndoManager', + + doFeaturesUpdate: function(context) { + this.updateRecords(context); + }, + + doFeaturesUndo: function(context) { + this.doRecordUndo(context); + }, + doFeaturesRedo: function(context) { + this.doRecordRedo(context); + }, + + featuresDidUpdate: function(context) { + Footprint.statechart.sendAction('doClearSelection', Footprint.layerSelectionEditController); + }, + + /*** + * Responds to identify by calling doQueryRecords + */ + doFeatureIdentify: function() { + // Trigger the modal state to open the query dialog + this.statechart.sendAction('doQueryRecords', + SC.ObjectController.create({ + recordType:Footprint.featuresActiveController.get('recordType'), + infoPane: 'Footprint.FeatureInfoPane', + nowShowing:'Footprint.FeatureSummaryInfoView', + recordsEditController:Footprint.featuresEditController + }) + ); + }, + + /*** + * Just clears the bounds without saving + doFeatureEdit: function() { + this.statechart.sendAction('doUpdateRecord', + SC.ObjectController.create({ + recordType:Footprint.featuresActiveController.get('recordType'), + nowShowing:'Footprint.FeatureEditInfoView', + recordsEditController:Footprint.featuresEditController + }) + ); + }, + + /*** + * Handles updating the features via painting + */ + doPaintApply: function() { + this.updateRecords(); + }, + + /*** + * Handles clearing the features + */ + doPaintClear: function() { + this.clearRecords(); + }, + + enterState: function(context) { + this._nestedStore = Footprint.store.chainAutonomousStore(); + if (context.get('length') > 0) { + // When there are features we loaded the nested store versions + this._content = this._nestedStore.find(SC.Query.local( + context.get('recordType'), { + conditions: '{storeKeys} CONTAINS storeKey', + storeKeys:context.mapProperty('storeKey') + })); + // Enable the info, apply, clear, etc buttons + Footprint.toolController.set('featurerIsEnabled', YES); + } + else { + // If no features, we are just here to support undo/redo + this._content = []; + } + this._context = SC.ArrayController.create({content: this._content, recordType: context.get('recordType'), layer: context.get('layer')}); + Footprint.featuresEditController.set('content', this._context.get('content')); + sc_super() + }, + + /*** + * Creates an active painting context for the selected features and the controller settings + */ + updateContext: function(context) { + var recordContext = SC.ObjectController.create({ + built_form: Footprint.builtFormActiveController.get('content'), + dev_pct: Footprint.paintingController.get('developmentPercent'), + density_pct: Footprint.paintingController.get('densityPercent'), + total_redev: Footprint.paintingController.get('isFullRedevelopment') + }); + return this.createModifyContext(recordContext, context); + }, + + /*** + * Creates an clear painting context for the selected features and the controller settings. + */ + clearContext: function() { + var recordContext = SC.ObjectController.create({ + built_form: null, + dev_pct: 1, + density_pct: 1, + total_redev: NO + }); + return this.createModifyContext(recordContext, + this._context || SC.ObjectController.create({content:this._content})); + } +}); diff --git a/sproutcore/apps/fp/states/selection_states/layer_selection_edit_state.js b/sproutcore/apps/fp/states/selection_states/layer_selection_edit_state.js new file mode 100644 index 000000000..4740e36b8 --- /dev/null +++ b/sproutcore/apps/fp/states/selection_states/layer_selection_edit_state.js @@ -0,0 +1,95 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.LayerSelectionEditState = SC.State.extend({ + initialSubstate: 'choicepointState', + + /*** + * On entry, decides which peer substate we should end up in. + */ + choicepointState: SC.State.extend({ + enterState: function(layerContext) { + if (!(Footprint.layerLibraryActiveController.getPath('layers.status') & SC.Record.READY) || + // Hack to keep background layers from trying to load layer selection + // When we have a better definition of DbEntity functional types, we'll use that to prevent loading + Footprint.layerActiveController.get('tags').mapProperty('tag').contains('background_imagery')) { + Footprint.statechart.gotoState('layerNotReadyState', layerContext); + } + else if (!(Footprint.layerSelectionActiveController.get('status') & SC.Record.READY)) { + // TODO temp fix for weird binding problem--this assignment isn't binding reliably + Footprint.mapLayerGroupsController.set('activeLayer', Footprint.layerActiveController.get('content')); + Footprint.statechart.gotoState('loadingLayerSelectionsState', layerContext); + } + else { + // TODO temp fix for weird binding problem--this assignment isn't binding reliably + Footprint.mapLayerGroupsController.set('activeLayer', Footprint.layerActiveController.get('content')); + Footprint.statechart.gotoState('layerSelectionIsReadyState', SC.ObjectController.create({content:layerContext})); + } + } + }), + + /*** + * No layer selection because no layer! Once it loads, we will reenter choicepointState. + */ + layerNotReadyState: SC.State.extend({ + enterState: function() { + Footprint.toolController.set('featurerIsEnabled', NO); + Footprint.toolController.set('selectorIsEnabled', NO); + } + }), + + /*** + * Layer selection is loading. + */ + loadingLayerSelectionsState: Footprint.LoadingState.extend({ + enterState: function(context) { + Footprint.toolController.set('featurerIsEnabled', NO); + Footprint.toolController.set('selectorIsEnabled', NO); + return sc_super(); + }, + recordType:Footprint.LayerSelection, + loadingController: Footprint.layerSelectionsController, + didLoadEvent:'layerSelectionsControllerIsReady', + didFailEvent:'layerSelectionsControllerDidFail', + + recordArray: function(context) { + // TODO there's no need to load the LayerSections every time we enter the state + // They should be cached once loaded--it's unlikely they will be updated outside this app in the meantime + //var configEntity = Footprint.scenarioActiveController.get('content'); + var layer = context.get('content'); + if (layer) { + return Footprint.store.find(SC.Query.create({ + recordType: this.get('recordType'), + location: SC.Query.REMOTE, + parameters: { + layer:layer + } + })); + } + }, + + layerSelectionsControllerIsReady: function() { + // Start over now that we have a layerSelection + // invokeLast to allow the active controller to bind + this.invokeNext(function() { + this.gotoState(this.getPath('parentState.parentState.fullPath'), Footprint.layerSelectionsController.getPath('selection.firstObject')); + }); + } + }), + + /*** + * Layer selection is ready! + */ + layerSelectionIsReadyState: SC.State.plugin('Footprint.LayerSelectionIsReadyState') +}); diff --git a/sproutcore/apps/fp/states/selection_states/layer_selection_is_ready_state.js b/sproutcore/apps/fp/states/selection_states/layer_selection_is_ready_state.js new file mode 100644 index 000000000..96db55288 --- /dev/null +++ b/sproutcore/apps/fp/states/selection_states/layer_selection_is_ready_state.js @@ -0,0 +1,429 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('controllers/features/feature_controllers'); +sc_require('states/record_updating_state'); +sc_require('states/selection_states/querying_state'); + +/*** + * Handles updating a layerSelection instance by drawing bounds or querying + * The context is always a controller whose content is a layerSelection instance + * @type {*|RangeObserver|Class|void} + */ +Footprint.LayerSelectionIsReadyState = Footprint.RecordsAreReadyState.extend({ + + + observer: function(key, value, foo) { + logWarning(key); + }.observes('Footprint.layerSelectionEditController.*'), + + recordsDidUpdateEvent: 'layerSelectionDidUpdate', + recordsDidFailToUpdateEvent: 'layerSelectionDidFailToUpdate', + updateAction: 'doLayerSelectionUpdate', + undoAction: 'doLayerSelectionUndo', + undoAttributes: ['query_strings', 'joins', 'bounds'], + + /*** + * Each layerSelection has an undoManager + */ + undoManagerProperty: 'undoManager', + + doLayerSelectionUndo: function(context) { + this.doRecordUndo(context); + }, + doLayerSelectionRedo: function(context) { + this.doRecordRedo(context); + }, + + /*** + * Update the layerSelection + * @param context + */ + doUpdateLayerSelection: function(context) { + this.updateRecords(context); + }, + + /*** + * Clears the selection and saves the clear layerSelection to the server + */ + doClearSelection: function() { + // Clear the filter unless the modal feature pane is up. This should be moved somewhere called less + Footprint.statechart.sendAction('doClearQueryAttributes'); + this.clearRecords(self._context); + }, + + + /*** + * Creates an update context for layer_selections. Since layer_selections are updated by query form and selecting + * bounds, this doesn't set any attributes to update. + */ + updateContext: function(context) { + var recordContext = SC.ObjectController.create({selectionWantsToEnd:context.get('selectionWantsToEnd')}); + return this.createModifyContext(recordContext, context); + }, + + /*** + * Creates an clear painting context for the selected features and the controller settings. + */ + clearContext: function(context) { + var recordContext = SC.ObjectController.create({ + bounds: null, + query_strings: SC.Object.create({filter_string:null, aggregates_string:null, group_by_string:null}), + joins: null + }); + return this.createModifyContext(recordContext, context) + }, + + /*** + * Responds to a PaintTool beginning to draw selection shape on the map. + * @param view + */ + doStartSelection: function() { + this.setBounds(); + // Clear the filter unless the modal feature pane is up. This should be moved somewhere called less + Footprint.statechart.sendAction('doClearFilterUnlessModal'); + }, + + /*** + * Responds to a PaintTool adding a coordinate to its selection shape. + * The closed geometry of the new shape is assigned to the activeLayerSelection.bounds + * Commits the selection continually. + * @param view + */ + doAddToSelection: function() { + // Set the bounds to a new SC.Object so that we can observe changes in the UI + this.setBounds(); + Footprint.statechart.sendAction('doTestSelectionChange', SC.ObjectController.create( + {content:this._context.get('content'), selectionWantsToEnd:NO}) + ); + }, + + /*** + * Responds to a PaintTool ending its selection shape. + * The closed geometry of the final shape is assigned to the activeLayerSelection.bounds + * @param view + */ + doEndSelection: function() { + // Set the bounds to a new SC.Object so that we can observe changes in the UI + this.setBounds(); + // Pass the desire to end on the context + Footprint.statechart.sendAction('doTestSelectionChange', SC.ObjectController.create( + {content:this._context.get('content'), selectionWantsToEnd:YES}) + ); + }, + + // Stores the most recently selected bounds so that when a new doTestSelectionChange happens we + // can check to see if the bounds actually changed + _bounds:null, + _time:null, + + // Triggered by a timer (box) or points (polygon) or whatever. Is + // also run when entering the state in case the layer selection is already set to something + doTestSelectionChange: function(context) { + var time = new Date().getTime(); + // Just do at end for now + //(NO && time-this._time > 3000) || + if (context.get('selectionWantsToEnd')) { + // If a change occurred since last save (geoms, query doesn't match, etc), + // and a decent amount of time has passed or the selection wants to end, + // goto the savingSelectionState substate in order to update the server + // The context might include a selectionWantsToEnd, which savingSelectionState will handle + this._time = time; + this._bounds = context.get('bounds'); + // Save the bounds query + if (context.get('selectionWantsToEnd')) { + this._invokeContext = this._invokeContext || context; + this.invokeOnce('onceUpdateLayerSelection'); + } + } + }, + onceUpdateLayerSelection: function() { + Footprint.statechart.sendAction('doUpdateLayerSelection', this._invokeContext); + this._invokeContext = null; + }, + + /*** + * Set the bounds to the painted geometry + * @param bounds + */ + setBounds: function() { + if (!Footprint.mapToolsController.get('activePaintTool')) { + logWarning('No active paint tool. This should not happen'); + return; + } + var bounds = SC.Object.create(Footprint.mapToolsController.get('activePaintTool').geometry()); + this._context.set('bounds', bounds); + }, + + /*** + * Execute the query defined in the layerSelection + * @param context + */ + doExecuteQuery: function(context) { + this.gotoState('queryingState', SC.ObjectController.create({content:context.get('content')})); + }, + + /*** + * Open the query window + * @param view + */ + doFeatureQuery: function() { + if (!((this._context.get('status') & SC.Record.READY) && + (Footprint.featuresActiveController.getPath('layerStatus') & SC.Record.READY))) { + logWarning("doFeatureQuery called when dependant controllers were not ready. This should not be possible."); + return; + } + // Trigger the modal state to open the query dialog + this.statechart.sendAction('doQueryRecords', + SC.ObjectController.create({ + recordType:Footprint.featuresActiveController.get('recordType'), + infoPane: 'Footprint.FeatureInfoPane', + nowShowing:'Footprint.FeatureQueryInfoView', + recordsEditController:Footprint.featuresEditController + }) + ); + }, + + /*** + * Just clears the bounds without saving + */ + doClearBounds: function() { + this.clear(['bounds']); + }, + /*** + * Just clears the filter without saving + */ + doClearFilter: function() { + this.clear(['query_strings.filter_string', 'filter']); + }, + + doClearJoins: function() { + this.clear(['joins']); + }, + + /*** + * Just clears the aggregate fields without saving + */ + doClearAggregates: function() { + this.clear(['query_strings.aggregates_string', 'aggregates']); + }, + + /*** + * Just clears the aggregate fields without saving + */ + doClearGroupBy: function() { + this.clear(['query_strings.group_by_string', 'group_bys']); + }, + + /*** + * Clears given layer selection properties + * @param properties - simple or chained property string + * @returns {*} + */ + clear: function(properties) { + var context = this._context; + if (context.getPath('store').readStatus(context.get('storeKey')) & SC.Record.BUSY) { + logWarning("Attempt to clear layerSelection while it is busy"); + return; + } + properties.forEach(function(property) { + context.setPath(property, null); + }); + }, + + // Tell the map controller whenever a new selection layer is ready + layerSelectionDidUpdate:function(context) { + Footprint.mapController.refreshSelectionLayer(); + if (context.get('selectionWantsToEnd')) { + // Send the layerSelection as the context + Footprint.statechart.sendEvent('selectionDidEnd', context); + } + else { + this.gotoState('%@.readyState'.fmt(this.get('fullPath')), context); + } + }, + + // Event thrown by substates when they've decided that we're ready to end selecting. + selectionDidEnd: function(context) { + // Start over + this.gotoState(this.get('fullPath'), context); + }, + + layerSelectionDidFailToUpdate: function(context) { + // If selection wants to end, throw an error message to the user. + if (context && context.get('selectionWantsToEnd')) { + SC.AlertPane.error({ + message: 'A selection error occurred', + description: 'There was an error processing your selection. You can try selecting fewer features.' + }); + } + // Make sure all the bindings are correct + + // Either way, run selectionDidUpdate. + // slight hack... an errored selection behaves the same as an updated selection + error message... so + // we keep it internal here rather than routing it through an action call. + this.layerSelectionDidUpdate(context); + }, + + enterState: function(context) { + this._nestedStore = Footprint.store.chainAutonomousStore(); + this._content = this._nestedStore.materializeRecord(Footprint.layerSelectionActiveController.get('storeKey')); + this._context = SC.ObjectController.create({content: this._content, selectionWantsToEnd: context.get('selectionWantsToEnd') || NO}); + sc_super() + + Footprint.toolController.set('selectionToolNeedsReset', YES); + + // Use the controller for ease of reference. + Footprint.layerSelectionEditController.set('content', this._content); + + // No Feature Footprint.featuresActiveController.get('content'))inspection or updating is allowed until we have downloaded the features + Footprint.toolController.set('featurerIsEnabled', NO); + // Enable selector tools + Footprint.toolController.set('selectorIsEnabled', YES); + }, + + /*** + * Override the parent state's readyState to advance us to the selectedFeaturesState whenever there are features + * in the layerSelection + */ + readyState: SC.State.extend({ + enterState: function(context) { + // Clear the features. We will either load them next if there are some in the + // layer_selection or do nothing + if (context.getPath('features.length')) { + // Features selected. + Footprint.statechart.gotoState('selectedFeaturesState', this._context); + } + else { + // Set it to empty so the query info window shows no results + Footprint.featuresActiveController.set('content', []); + // Still go to the featuresAreReadyState so that we can support undo/redo + this.gotoState('featuresAreReadyState', Footprint.featuresActiveController); + } + } + }), + + exitState: function() { + if (this._nestedStore) + this._nestedStore.destroy(); + this._nestedStore = null; + this._content = null + Footprint.layerSelectionEditController.set('content', null); + this._context = null; + }, + + // TODO Overrriding parent version to force action handling + updatingState: Footprint.RecordUpdatingState.extend({ + doStartSelection: function() { + this.cancelUpdate(); + return NO; + }, + doEndSelection: function() { + this.cancelUpdate(); + return NO; + }, + doAddToSelection: function() { + this.cancelUpdate(); + return NO; + }, + doExecuteQuery: function() { + this.cancelUpdate(); + return NO; + }, + doClearSelection: function() { + this.cancelUpdate(); + return NO; + }, + doQueryRecords: function() { + this.cancelUpdate(); + return NO; + }, + + undoActionBinding: SC.Binding.oneWay('.parentState.undoAction'), + updateActionBinding: SC.Binding.oneWay('.parentState.updateAction'), + recordsDidUpdateEventBinding: SC.Binding.oneWay('.parentState.recordsDidUpdateEvent'), + recordsDidFailToUpdateEventBinding: SC.Binding.oneWay('.parentState.recordsDidFailToUpdateEvent'), + recordsDidUpdate:function() { + // Do the default stuff + sc_super(); + } + }), + + // The state for querying for selections + queryingState:Footprint.QueryingState.extend({ + queryDidValidate: function(context) { + // Never send this stuff + if (Footprint.layerSelectionEditController.getPath("query_string.group_by_string") ^ + Footprint.layerSelectionEditController.getPath("group_by") + ) + logError('Out of sync state of groupby for layerSelection: %@'.fmt(Footprint.layerSelectionEditController.get('content').toString())) + if (Footprint.layerSelectionEditController.getPath("query_string.aggregates_string") ^ + Footprint.layerSelectionEditController.getPath("aggregates") + ) + logError('Out of sync state of aggregates for layerSelection: %@'.fmt(Footprint.layerSelectionEditController.get('content').toString())) + Footprint.statechart.sendAction('doUpdateLayerSelection', SC.ObjectController.create(context)); + }, + queryDidFail: function(context) { + } + }), + + // When a selection is ready, we load the features and then allow the user to edit them (multiple + // times). + selectedFeaturesState:SC.State.extend({ + + initialSubstate:'loadingFeaturesState', + + enterState: function(context) { + this._context = context; + }, + exitState: function() { + this._context = null; + }, + + /*** + * Load the features stored in the layerSelection + */ + loadingFeaturesState: Footprint.LoadingState.extend({ + + didLoadEvent:'featuresDidLoad', + loadingController:Footprint.featuresActiveController, + setLoadingControllerDirectly: NO, + + enterState: function(context) { + sc_super(); + }, + + /*** + * Fetches the features in the Footprint.layerSelectionActive controller via a remote query + * @returns {*} + */ + recordArray: function() { + return Footprint.store.find(SC.Query.create({ + recordType:Footprint.layerSelectionEditController.getPath('selection_layer.featureRecordType'), + location:SC.Query.REMOTE, + parameters:{ + // We use the layer_selection instead of listing all the feature ids, to prevent + // overly long URLs + layer:Footprint.layerSelectionEditController.get('selection_layer') + } + })); + }, + featuresDidLoad: function() { + // Look over all in-flight saves. If any of them include features that overlap with the current set: + this.gotoState('featuresAreReadyState', Footprint.featuresActiveController); + } + }), + + featuresAreReadyState: SC.State.plugin('Footprint.FeaturesAreReadyState') + }) +}); diff --git a/sproutcore/apps/fp/states/selection_states/querying_state.js b/sproutcore/apps/fp/states/selection_states/querying_state.js new file mode 100644 index 000000000..90b32461e --- /dev/null +++ b/sproutcore/apps/fp/states/selection_states/querying_state.js @@ -0,0 +1,97 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +Footprint.QueryingState = SC.State.extend({ + + initialSubstate:'readyState', + + readyState: SC.State.extend({ + enterState: function(context) { + if (this.parseQuery(Footprint.layerSelectionEditController)) { + Footprint.statechart.sendAction('queryDidValidate', Footprint.layerSelectionEditController); + } + else { + Footprint.statechart.sendAction('queryDidFail', Footprint.layerSelectionEditController); + } + }, + + parseQuery: function(context) { + + // Parse the filter string using our enhanced SCQL + // TODO prevent empty string weirdness + if (!context.getPath('query_strings.filter_string')) + context.setPath('query_strings.filter_string', null); + var filterString = context.getPath('query_strings.filter_string'); + var filter = null; + if (filterString) { + var filter = processQuery(filterString); + if (filter.error) { + SC.AlertPane.warn({ + message: "Could not parse query", + description: "Only basic operators are currently available: '>, <, ='. You can refer to properties by name. Example: built_form.name = 'Agriculture'" + }); + Footprint.statechart.gotoState(this.get('fullPath'), context); + return NO; + } + } + Footprint.layerSelectionEditController.set('filter', filter); + + // Parse each comma-separated aggregate in the aggregatesString using our enhanced SCQL + // TODO prevent empty string weirdness + if (!context.getPath('query_strings.aggregates_string')) + context.setPath('query_strings.aggregates_string', null); + var aggregatesString = context.getPath('query_strings.aggregates_string'); + try { + Footprint.layerSelectionEditController.set('aggregates', aggregatesString ? aggregatesString.split(',').map(function(aggregate) { + // Parse each separate for now, since SCQL can't handle selection strings + var aggregateQuery = processQuery(aggregate); + if (aggregateQuery.error) { + SC.AlertPane.warn({ + message: "Could not parse aggregate field", + description: "Only SUM(field) AVG(field) and COUNT(field) are currently supported. Multiples may be separated by commas, such as SUM(du), AVG(emp)" + }); + Footprint.statechart.gotoState(this.get('fullPath'), context); + throw "give up"; + } + return aggregateQuery; + }) : null); + } + catch(e) { + return NO; + } + + // Parse the single groupBy string in groupByString using our enhanced SCQL + if (!context.getPath('query_strings.group_by_string')) + context.setPath('query_strings.group_by_string', null); + var groupByString = context.getPath('content.query_strings.group_by_string'); + try { + Footprint.layerSelectionEditController.set('group_bys', groupByString ? groupByString.split(',').map(function(groupBy) { + var groupByQuery = processQuery(groupBy); + if (groupByQuery.error ) { + SC.AlertPane.warn({ + message: "Could not parse group by text", + description: "This should be comma-separated properties of the main feature table or that specified by join. Example, land_use_code or built_form.id" + }); + Footprint.statechart.gotoState(this.get('fullPath'), context); + throw "give up"; + } + return groupByQuery; + }): null); + } + catch(e) { + return NO; + } + return YES; + } + }) +}); diff --git a/sproutcore/apps/fp/states/test_states.js b/sproutcore/apps/fp/states/test_states.js new file mode 100644 index 000000000..1a7c064e8 --- /dev/null +++ b/sproutcore/apps/fp/states/test_states.js @@ -0,0 +1,96 @@ +/** + * Created by calthorpe on 11/15/13. + */ +sc_require('views/main_pane'); +sc_require('states/logging_in_state'); + +if (!Footprint.DO_STATE_TESTS) { + Footprint.TestState = SC.State; + Footprint.TestAppState = SC.State; +} +else { +Footprint.TestState = SC.State.extend({ + initialSubstate: 'loadingState', + + // Override this with the needed dependencies + loadingState: Footprint.LoadingConcurrentDependenciesState, + + readyState: SC.State.extend({ + substatesAreConcurrent: YES, + modalState:SC.State.plugin('Footprint.CrudState'), + + enterState: function() { + Footprint.mainPage.get('mainPane').append(); + }, + exitState: function() { + Footprint.mainPage.get('mainPane').remove(); + } + }) + +}); + +Footprint.TestAppState = SC.State.extend({ + + initialSubstate:'readyState', + readyState: SC.State, + + // Test to make sure that the Footprint.LabelSelectView functions (test it on BuiltForm) + testLabelSelectViewState: Footprint.TestState.extend({ + loadingState: Footprint.LoadingConcurrentDependenciesState.extend({ + didLoadConcurrentDependencies: function() { + Footprint.statechart.gotoState('%@.readyState'.fmt(this.getPath('parentState.name'))); + }, + loadingBuiltFormSetsState: SC.State.plugin('Footprint.LoadingBuiltFormSetsState'), + loadingBuiltFormTagsState:SC.State.plugin('Footprint.LoadingBuiltFormTagsState') + }) + }), + + // Test to make sure that the Footprint.EditButtonView menu works (test it on BuiltForm) + testActionMenuState: Footprint.TestState.extend({ + loadingState: Footprint.LoadingConcurrentDependenciesState.extend({ + didLoadConcurrentDependencies: function() { + Footprint.statechart.gotoState('%@.readyState'.fmt(this.getPath('parentState.name'))); + }, + loadingBuiltFormSetsState: SC.State.plugin('Footprint.LoadingBuiltFormSetsState'), + loadingBuiltFormTagsState:SC.State.plugin('Footprint.LoadingBuiltFormTagsState') + }) + }), + + // Test to make sure that Scenario cloning works + testScenarioClone: Footprint.TestState.extend({ + loadingState: Footprint.LoadingConcurrentDependenciesState.extend({ + didLoadConcurrentDependencies: function() { + Footprint.statechart.gotoState('%@.readyState'.fmt(this.getPath('parentState.name'))); + }, + + loadingScenarioCategoriesState: SC.State.plugin('Footprint.LoadingScenarioCategoriesState') + }) + }), + + testScenario: Footprint.TestState.extend({ + + }) +}); + +Footprint.testController = SC.ObjectController.create({ + content: ['testLabelSelectViewState'] +}); +Footprint.MainPane.reopen({ + childViews: 'testTitle projectSectionView leftlogoView rightlogoView accountView splitView'.w(), + + testTitle: SC.LabelView.extend({ + classNames: ['footprint-test-title'], + layout: {left:0.7, width:0.2, height:24}, + valueBinding:SC.Binding.oneWay('Footprint.testController.content').transform(function(value) { + if (value) + return 'Running Test: %@'.fmt(value); + }) + }) +}); + +Footprint.LoggingInState.reopen({ + didCompleteLogin: function() { + this.gotoState('testLabelSelectViewState'); + } +}); +} \ No newline at end of file diff --git a/sproutcore/apps/fp/states/tool_states/showing_tools_state.js b/sproutcore/apps/fp/states/tool_states/showing_tools_state.js new file mode 100644 index 000000000..5d4ce3dde --- /dev/null +++ b/sproutcore/apps/fp/states/tool_states/showing_tools_state.js @@ -0,0 +1,6 @@ +/*** + * The state that manages the projects pane at the top of the application + * @type {Class} + */ +Footprint.ShowingToolsState = SC.State.design({ +}); diff --git a/sproutcore/apps/fp/tests/integration/controllers/built_form_controllers_test.js b/sproutcore/apps/fp/tests/integration/controllers/built_form_controllers_test.js new file mode 100644 index 000000000..8e5ca41b9 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/controllers/built_form_controllers_test.js @@ -0,0 +1,30 @@ +// ========================================================================== +// Project: Footprint Unit Test +// Copyright: @2013 My Company, Inc. +// ========================================================================== +/*globals Footprint module test ok equals same stop start */ + + +module("Footprint.builtFormsControllers", { + setup: function() { + setupApplicationForControllerTesting( + { + stateSetup: bypassLoginState + } + ); + }, + teardown: controllerTeardown +}); + + +test("Testing builtFormCategoriesTreeController", function() { + // Give the test enough time to complete + stop(30000); + var params = { + controllers: Footprint.builtFormControllers, + controllersPath: 'Footprint.builtFormControllers', + recordType: Footprint.BuiltForm + }; + Footprint.TestForBuiltFormControllers = testTreeController(params); +}); + diff --git a/sproutcore/apps/fp/tests/integration/controllers/config_entity_controllers_test.js b/sproutcore/apps/fp/tests/integration/controllers/config_entity_controllers_test.js new file mode 100644 index 000000000..21141c22a --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/controllers/config_entity_controllers_test.js @@ -0,0 +1,48 @@ +// ========================================================================== +// Project: Footprint Unit Test +// Copyright: @2013 My Company, Inc. +// ========================================================================== + +module("Footprint.scenarioController", { + setup: function() { + setupApplicationForControllerTesting( + { + stateSetup:bypassLoginState + } + ); + }, + + teardown: function() { + controllerTeardown(); + } +}); + +test("Testing scenarioController", function() { + + ok('test'); + // Give the test enough time to complete the asynchronous timeout handlers + stop(20000); + + var timeout=0; + setTimeout(function() { + logStatus(Footprint.globalConfigController, 'Footprint.globalConfigController'); + assertStatus(Footprint.globalConfigController, Footprint.Record.READY_CLEAN, 'Footprint.globalConfigController'); + }, timeout+=2000); + + setTimeout(function() { + logStatus(Footprint.regionsController, 'Footprint.regionsController'); + assertStatus(Footprint.regionsController, Footprint.Record.READY_CLEAN, 'Footprint.regionsController'); + }, timeout+=2000); + + setTimeout(function() { + logStatus(Footprint.projectsController, 'Footprint.projectsController'); + assertStatus(Footprint.projectsController, Footprint.Record.READY_CLEAN, 'Footprint.projectsController'); + }, timeout+=2000); + + setTimeout(function() { + logStatus(Footprint.scenariosController, 'Footprint.scenariosController'); + assertStatus(Footprint.scenariosController, Footprint.Record.READY_CLEAN, 'Footprint.scenariosController'); + start(); + }, timeout+=5000); + +}); diff --git a/sproutcore/apps/fp/tests/integration/controllers/library_controllers_test.js b/sproutcore/apps/fp/tests/integration/controllers/library_controllers_test.js new file mode 100644 index 000000000..5f5e82099 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/controllers/library_controllers_test.js @@ -0,0 +1,49 @@ +// ========================================================================== +// Project: Footprint Unit Test +// Copyright: @2013 My Company, Inc. +// ========================================================================== + +/** + * Test the controllers manage the library of DbEntityInterests of the active ConfigEntity, and possibly other libraries, such as lists of charts, etc. + */ +module("Footprint.layLibraryControllers", { + setup: function() { + setupApplicationForControllerTesting( + { + stateSetup:bypassLoginState + } + ); + }, + + teardown: controllerTeardown +}); + +test("Testing layersController", function() { + + // Give the test enough time to complete + stop(30000); + var params = { + controllers: Footprint.layerControllers, + controllersPath: 'Footprint.layersControllers', + recordType: Footprint.PresentationMedium + }; + Footprint.TestForLaryLibraryControllers = testListController(params); + + setTimeout(function() { + // We exepct one PresentationMedium per db_entity + var expectedLength = Footprint.scenarioActiveController.getPath('content.db_entities').length; + ok(expectedLength > 0, "No DbEntities found for the active ConfigEntity"); + assertLength( + expectedLength, + Footprint.layersController.get('content'), + 'Footprint.layersController.content'); + assertKindForList( + Footprint.PresentationMedium, + Footprint.layersController.get('content'), + 'Footprint.layersController.content'); + // Restart the main thread + start(); + }, 8000); +}); + + diff --git a/sproutcore/apps/fp/tests/integration/controllers/login_test.js b/sproutcore/apps/fp/tests/integration/controllers/login_test.js new file mode 100644 index 000000000..e18a1a7d8 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/controllers/login_test.js @@ -0,0 +1,37 @@ +// ========================================================================== +// Project: Footprint.loginController Unit Test +// Copyright: @2012 My Company, Inc. +// ========================================================================== + + +module("Footprint.loginController", { + setup: function() { + setupApplicationForControllerTesting( + { + stateSetup: function() { + // NOTE: loginContent and the Login model no longer exist. - Dave P + login = Footprint.store.find(SC.Query.local(Footprint.Login)).toArray()[0]; + Footprint.loginContent.set('username', login.get('username')) + Footprint.loginContent.set('password', login.get('password')) + } + } + ); + }, + + teardown: controllerTeardown +}); + + +test("Testing userController", function() { + + // Set the loginContent to simulate the form entry + // Wait for the server + stop(3000); + setTimeout(function() { + SC.RunLoop.begin(); + SC.RunLoop.end(); + assertStatus(Footprint.userController.content, Footprint.Record.READY_CLEAN, 'Footprint.userController'); + assertCurrentState(Footprint.LoadingApp); + start(); + }, 1000); +}); diff --git a/sproutcore/apps/fp/tests/integration/controllers/policy_controllers_test.js b/sproutcore/apps/fp/tests/integration/controllers/policy_controllers_test.js new file mode 100644 index 000000000..7111a6575 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/controllers/policy_controllers_test.js @@ -0,0 +1,38 @@ +// ========================================================================== +// Project: Footprint Unit Test +// Copyright: @2013 My Company, Inc. +// ========================================================================== +/*globals Footprint module test ok equals same stop start */ + + +module("Footprint.policiesControllers", { + setup: function() { + setupApplicationForControllerTesting( + { + stateSetup: bypassLoginState + } + ); + }, + + teardown: controllerTeardown +}); + +test("Testing policyCategoriesTreeController", function() { + + // Give the test enough time to complete the asynchronous timeout handlers + stop(10000); + + setTimeout(function() { + // Verify that PolicySet instances at the first level of the tree + var policyCategories = Footprint.policyCategoriesTreeController.get('treeItemChildren'); + assertNonZeroLength(policyCategories, 'Footprint.policyCategoriesTreeController.arrangedObjects'); + assertKindForList(Footprint.PolicyCategory, policyCategories, 'Footprint.policyCategoriesTreeController.arrangedObjects'); + // Verify that Policy instances at the second level of the tree + assertNonZeroLength(policyCategories[0].get('treeItemChildren'), + 'Footprint.policyCategoriesTreeController.treeItemChildren[0].treeItemChildren'); + assertKindForList(Footprint.Policy, policyCategories[0].get('treeItemChildren'), + 'Footprint.policyCategoriesTreeController.treeItemChildren[0].treeItemChildren'); + // Restart the main thread + start(); + }, 2400); +}); diff --git a/sproutcore/apps/fp/tests/integration/views/analytic_bar_view_test.js b/sproutcore/apps/fp/tests/integration/views/analytic_bar_view_test.js new file mode 100644 index 000000000..512287e3e --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/views/analytic_bar_view_test.js @@ -0,0 +1,13 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ diff --git a/sproutcore/apps/fp/tests/integration/views/built_form_set_view_test.js b/sproutcore/apps/fp/tests/integration/views/built_form_set_view_test.js new file mode 100644 index 000000000..2352565e8 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/views/built_form_set_view_test.js @@ -0,0 +1,51 @@ +/* + * urbanfootprint-california (v1.0), land use scenario development and modeling system. + * + * copyright (c) 2012 calthorpe associates + * + * this program is free software: you can redistribute it and/or modify it under the terms of the gnu general public license as published by the free software foundation, version 3 of the license. + * + * this program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. see the gnu general public license for more details. + * + * you should have received a copy of the gnu general public license along with this program. if not, see . + * + * contact: joe distefano (joed@calthorpe.com), calthorpe associates. firm contact: 2095 rose street suite 201, berkeley ca 94709. phone: (510) 548-6800. web: www.calthorpe.com + */ +sc_require('views/builtformsview'); +var pane, view; + +module("footprint.builtformsview", { + setup: function () { + pane = viewSetup({ + contentSetup: function () { + }, + views: function () { + return [Footprint.BuiltFormsView.extend({ + layout: { top: 0, width: .25 } + })]; + } + }); + view = pane.childViews[0]; + }, + + teardown: function () { + viewTeardown(); + } +}); + +test("Check the tree view bound to the BuiltForm data", function () { + + // Make sure the controller has content + stop(1500000); // delay main thread up to a second to allow breakpoints to work + + throw "die so we can interact"; + var contentViewPath = 'listView.contentView'; + var params = { + controllers: Footprint.builtFormControllers, + controllersPath: 'Footprint.builtFormControllers', + contentView: view.getPath(contentViewPath), + contentViewPath: 'Footprint.BuiltFormsView.' + contentViewPath, + editbarView: view.toolbarView.editbarView + }; + treeViewValidation(pane, params); +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/tests/integration/views/editable_model_string_view_test.js b/sproutcore/apps/fp/tests/integration/views/editable_model_string_view_test.js new file mode 100644 index 000000000..dcbbc202d --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/views/editable_model_string_view_test.js @@ -0,0 +1,59 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +var pane, view; + +/*** + * Tests the Footprint.LayerLibraryView data binding, event handling, and rendering. + */ +module("Footprint.EditableModelStringView", { + setup: function() { + pane = viewSetup({ + contentSetup: function() { + + Footprint.scenarioActiveController.set('content', + Footprint.store.find(SC.Query.local(Footprint.Scenario)).toArray()[0] + ); + }, + + views: function() { + return [Footprint.EditableModelStringView.extend({ + layout: { top:0, width:.30, height:20}, + valueBinding : 'Footprint.scenarioActiveController.name' + })]; + } + }); + view = pane.childViews[0]; + view.isEditingObserver = function() { + pane.$().css('position', 'relative'); + }.observes('Footprint.scenarioActiveController.name'); + }, + + teardown: viewTeardown() +}); + +test("Tests editing and binding of a Footprint.EditableModelStringView", function() { + + stop(1000000); // delay main thread up to a second to allow any breakpoints to work + // Make sure the controller has content + setTimeout(function() { + var value = view.get('value'); + var updatedValue = '%@__Test'.fmt(value); + editLabel(view); + // The inline editor resets positioning to absolute, hiding the test results + equals(updatedValue, view.get('value'), "Expected value %@ to become %@".fmt(value, updatedValue)); + start(); + + }, 1000); +}); diff --git a/sproutcore/apps/fp/tests/integration/views/library_view_test.js b/sproutcore/apps/fp/tests/integration/views/library_view_test.js new file mode 100644 index 000000000..5d4a27ce0 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/views/library_view_test.js @@ -0,0 +1,115 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +var pane, view; + +/*** + * Tests the Footprint.LayerLibraryView data binding, event handling, and rendering. + */ +module("Footprint.LayerLibraryView", { + setup: function() { + pane = viewSetup({ + contentSetup: function() { + // Set the project so that the Scenario controllers will set an active Scenario + Footprint.projectActiveController.set('content', + Footprint.store.find(SC.Query.local(Footprint.Project)).toArray()[0] + ); + }, + + views: function() { + return [Footprint.LayerLibraryView.extend({ + layout: { top:0, width:.30 } + })]; + } + }); + view = pane.childViews[0]; + }, + + teardown: viewTeardown() +}); + +test("Tests that policy set tree was created correctly", function() { + + stop(100000000000); // delay main thread up to a second to allow any breakpoints to work + var timeout=0; + // Make sure the controller has content + setTimeout(function() { + + listViewValidation(pane, { + listController: Footprint.layersController, + listControllerPath:'Footprint.layersController', + contentView: view.getPath('listView.contentView'), + contentViewPath: 'LayerLibraryView.listView.contentView', + itemValidator: function(i, itemView, presentationMedium) { + var nameView = itemView.nameView; + equals( + presentationMedium.getPath('db_entity_interest.name'), + nameView.get('value'), + 'Expecting a name for item index %@, representing instance %@'.fmt(i, presentationMedium.get('db_entity_interest').toString())); + var name = nameView.get('value'); + var updatedName = '%@__Test'.fmt(name); + editLabel(nameView, pane); + equals( + updatedName, + nameView.get('value'), + 'Expecting a view name to be updated to %@ for item index %@, representing instance %@'.fmt(updatedName, i, presentationMedium.get('db_entity_interest').toString())); + equals( + updatedName, + nameView.$().text(), + 'Expecting a view label text to be updated to %@ for item index %@, representing instance %@'.fmt(updatedName, i, presentationMedium.get('db_entity_interest').toString())); + } + }); + // Inline editing sets the positioning back to absolute + pane.$().css('position', 'relative'); + Footprint.libraryContent.get('selectedDbEntityInterestsByKey'); + throw 'p'; + start(); + }, timeout+=8000); +}); + +/* +test("Tests the VisibilityPicker views", function() { + stop(1000000); // delay main thread up to a second to allow any breakpoints to work + var timeout=0; + setTimeout(function() { + // Find the first SegmentedView + throw 'p'; + Footprint.store.find(SC.Query.local(Footprint.PresentationMedium)).mapProperty('id') + var segmentedView = findChildViewByKind(view, SC.SegmentedView); + equals(segmentedView.get('value'), Footprint.VISIBLE, 'Item should be visible'); + // Click the solo button to solo the item + var soloView = $('.sc-segment-view').get(0); + mouseClick(soloView); + equals(segmentedView.get('value'), Footprint.SOLO, 'Item should be soloing'); + + // Click the solo button again to unsolo the item + mouseClick(soloView); + equals(segmentedView.get('value'), Footprint.VISIBLE, 'Visible should be selected after unsoloing'); + + // Click the visible button again to hide the item + var visibleView = view.$('.sc-segment-view').get(1); + mouseClick(visibleView); + equals(segmentedView.get('value'), Footprint.HIDDEN, 'Item should be hidden after unvisiblizing'); + + // Solo the hidden item. It should be soloing + mouseClick(soloView); + equals(segmentedView.get('value'), Footprint.SOLO, 'Item should be soloing after being hidden'); + + // Unsolo the hidden item. It should go back to hidden + mouseClick(soloView); + equals(segmentedView.get('value'), Footprint.HIDDEN, 'Item should be hidden again after unsoloing'); + start(); + }, timeout+=5000); +}); +*/ diff --git a/sproutcore/apps/fp/tests/integration/views/main_pane_test.js b/sproutcore/apps/fp/tests/integration/views/main_pane_test.js new file mode 100644 index 000000000..bc4034c1c --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/views/main_pane_test.js @@ -0,0 +1,44 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + + +var pane, view; + +/** + * Tests the entire MainPane + */ +module("Footprint.MainPane", { + setup: function() { + pane = viewSetup({ + contentSetup: function() { + // Rather than explicity controller bindings like we usually do for view tests, just start the statechart and skip the login state + Footprint.statechart.initStatechart(); + bypassLoginState(); + }, + mainPane: Footprint.MainPane + }); + view = pane.childViews[0]; + }, + + teardown: viewTeardown() +}); + +test("Tests that policy set tree was created correctly", function() { + + stop(20000); // delay main thread to allow break points to take + // Make sure the controller has content + setTimeout(function() { +// start() + }, 1000); +}); diff --git a/sproutcore/apps/fp/tests/integration/views/policy_set_view_test.js b/sproutcore/apps/fp/tests/integration/views/policy_set_view_test.js new file mode 100644 index 000000000..5ff68e5d6 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/views/policy_set_view_test.js @@ -0,0 +1,59 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +var pane, view; + +module("Footprint.PolicySetView", { + setup: function() { + pane = viewSetup({ + contentSetup: function() { + Footprint.scenarioActiveController.set('content', + Footprint.store.find(SC.Query.local(Footprint.Scenario)).toArray()[0] + ); + setTimeout(function() { + }, 500); + }, + + views: function() { + return [Footprint.PolicySetView.extend({ + layout: { top:0, width:.50 } + })]; + } + }); + view = pane.childViews[0]; + }, + + teardown: viewTeardown() +}); + +test("Tests that policy set tree was created correctly", function() { + + stop(20000); // ddelay the main thread so the delayed validation can work below + // Make sure the controller has content + setTimeout(function() { + treeViewValidation(pane, { + treeController: Footprint.policyCategoriesTreeController, + treeControllerPath:'Footprint.policyCategoriesTreeController', + contentView: view.getPath('listView.contentView'), + contentViewPath: 'PolicySetView.listView.contentView', + topLevelValidator: function(i, itemView, policyCategory) { + }, + bottomLevelValidator: function(i, itemView, policy) { + ok(!isNaN(itemView.get('valueView').get('value')), 'Expecting a numeric value the value property for item index %@, representing instance %@'.fmt(i, policy.toString())); + } + }); + + start() + }, 1000); +}); diff --git a/sproutcore/apps/fp/tests/integration/views/project_titlebar_view_test.js b/sproutcore/apps/fp/tests/integration/views/project_titlebar_view_test.js new file mode 100644 index 000000000..2401ce7f2 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/views/project_titlebar_view_test.js @@ -0,0 +1,62 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +var pane, view; + +module("Footprint.ProjectToolbarView", { + setup: function () { + pane = viewSetup({ + contentSetup: function () { + stop(1000); + Footprint.regionActiveController.set('content', + Footprint.store.find(SC.Query.local(Footprint.Region)).toArray()[0], + setTimeout(function () { + Footprint.projectActiveController.set('content', Footprint.projectsController.toArray()[0]); + start(); + }, 500) + ); + }, + + views: function () { + return [Footprint.ProjectToolbarView.extend({ + })]; + } + }); + view = pane.childViews[0]; + }, + + teardown: viewTeardown() +}); + +test("Tests that policy set tree was created correctly", function () { + + stop(20000); // delay main thread to allow break points to take + // Make sure the controller has content + setTimeout(function () { + var data = { + listController: Footprint.projectsController, + listControllerPath: 'Footprint.projectsController', + contentView: view.getPath('buttonLayout.selectView'), + contentViewPath: 'ProjectToolbarView.selectView', + itemActiveController: Footprint.projectActiveController, + itemActiveControllerPath: 'Footprint.projectActiveController' + }; + // Validate the selectView, which changes the active project + selectViewValidation(pane, data); + // Validate the titleView, which simply displays the project name + var titleView = view.get('titleView'); + equals(titleView.get('value'), data.itemActiveController.getPath('content.name'), "Expect %@ label to match %@'s label".fmt('ProjectToolbarView.titleView', data.itemActiveControllerPath)); + start() + }, 1000); +}); diff --git a/sproutcore/apps/fp/tests/integration/views/results_view_test.js b/sproutcore/apps/fp/tests/integration/views/results_view_test.js new file mode 100644 index 000000000..eb1fe1791 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/views/results_view_test.js @@ -0,0 +1,49 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +var pane = null, view=null; +module("Footprint.ResultsView", { + + setup: function() { + + pane = viewSetup({ + contentSetup: function() { + Footprint.projectActiveController.set('content', + Footprint.store.find(SC.Query.local(Footprint.Project)).toArray()[0] + ); + Footprint.scenarioActiveController.set('content', + Footprint.store.find(SC.Query.local(Footprint.Scenario)).toArray()[0] + ); + }, + + views: function() { + return [Footprint.ResultSectionView.extend({ + layout: { top:0, width:.50 } + })]; + } + }); + view = pane.childViews[0]; + }, + + teardown: viewTeardown() +}); + +test("Tests that scenarios tree was created correctly", function() { + + stop(200000000); // delay main thread up to a second to allow any breakpoints to work + // Make sure the controller has content + setTimeout(function() { + throw "SDF"; + }, 10000); +}); diff --git a/sproutcore/apps/fp/tests/integration/views/scenarios_view_test.js b/sproutcore/apps/fp/tests/integration/views/scenarios_view_test.js new file mode 100644 index 000000000..a9d767fd4 --- /dev/null +++ b/sproutcore/apps/fp/tests/integration/views/scenarios_view_test.js @@ -0,0 +1,61 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +var pane = null, view = null; +module("Footprint.ScenariosView", { + + setup: function () { + + pane = viewSetup({ + contentSetup: function () { + Footprint.projectActiveController.set('content', + Footprint.store.find(SC.Query.local(Footprint.Project)).toArray()[0] + ); + }, + + scenariosLoaded: function () { + logStatus(Footprint.scenariosController, 'Footprint.scenariosController'); + if (Footprint.scenariosController.get('status') === Footprint.Record.READY_CLEAN) { + // Default to the first Scenario for now + Footprint.scenarioActiveController.set('content', Footprint.scenariosController.content.toArray()[0]); + } + }.observes('Footprint.scenariosController.status'), + + views: function () { + return [Footprint.ScenarioSectionView.extend({ + layout: { top: 0, width: .50 } + })]; + } + }); + view = pane.childViews[0]; + }, + + teardown: viewTeardown() +}); + +test("Tests that scenarios tree was created correctly", function () { + + // Make sure the controller has content + stop(20000); // delay main thread up to a second to allow breakpoints to work + + var contentViewPath = 'listView.contentView'; + var params = { + controllers: Footprint.scenarioControllers, + controllersPath: 'Footprint.scenarioControllers', + contentView: view.getPath(contentViewPath), + contentViewPath: 'Footprint.ScenariosView.' + contentViewPath, + editbarView: view.toolbarView.editbarView + }; + treeViewValidation(pane, params); +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/tests/unit/clone_tests.js b/sproutcore/apps/fp/tests/unit/clone_tests.js new file mode 100644 index 000000000..a931d7276 --- /dev/null +++ b/sproutcore/apps/fp/tests/unit/clone_tests.js @@ -0,0 +1,60 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +var scenario; +module("clone test", { + setup: function() { + setupForTesting(); + scenario = Footprint.store.find(Footprint.Scenario).objectAt(0); + } +}); + +test("Test nested fixtures", function() { + try { + + clonedScenario = scenario.cloneRecord(Footprint.store); + ok(clonedScenario.storeKey != null); + ok(scenario.storeKey != clonedScenario.storeKey); + + equals(scenario.get('parent_config_entity'), clonedScenario.get('parent_config_entity')); + + logProperty(clonedScenario.get('db_entity_interests').objectAt(0)); + ok(clonedScenario.get('db_entity_interests')); + equals(clonedScenario.get('db_entity_interests').length(), + scenario.get('db_entity_interests').length(), + 'Expect equal number of db_entity_interests'); + equals(clonedScenario.get('db_entity_interests').toArray()[0].get('config_entity'), clonedScenario); + + ok(clonedScenario.get('built_form_sets').length() > 0, 'Expect more than 0 built_form_sets'); + equals(clonedScenario.get('built_form_sets').length(), + scenario.get('built_form_sets').length(), + "Expect equal number of built_form_sets"); + + equals(clonedScenario.get('presentations').toArray()[0].get('config_entity'), clonedScenario); + // Account for garbage presentation that gets generated by fixtures + equals(clonedScenario.get('presentations').length(), + scenario.get('presentations').length()-1, + "Expect equal number of presentations"); + + equals($.map(clonedScenario.getPath('selections.db_entities'),function(key) { return key;}).length, + $.map(scenario.getPath('selections.db_entities'), function(key) { return key;}).length, + 'Expect equal number of db_entity selections'); + + } + catch(e) { + logError(e); + throw e; + } +}); diff --git a/sproutcore/apps/fp/views/cancel_button_view.js b/sproutcore/apps/fp/views/cancel_button_view.js new file mode 100644 index 000000000..40a3963d7 --- /dev/null +++ b/sproutcore/apps/fp/views/cancel_button_view.js @@ -0,0 +1,16 @@ +/** + * + * Created by calthorpe on 12/28/13. + */ + +Footprint.CancelButtonView = SC.ButtonView.design({ + layout: { height: 24, width: 80 }, + + calculatedStatus: null, + title: function() { + return this.get('calculatedStatus') === SC.Record.READY_DIRTY || + this.get('calculatedStatus') === SC.Record.READY_NEW ? + 'Cancel' : 'Done'; + }.property('calculatedStatus').cacheable(), + action: 'doPromptCancel' +}); diff --git a/sproutcore/apps/fp/views/categories_select_view.js b/sproutcore/apps/fp/views/categories_select_view.js new file mode 100644 index 000000000..26a17fd37 --- /dev/null +++ b/sproutcore/apps/fp/views/categories_select_view.js @@ -0,0 +1,24 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + + +/*** + * A someday multi-select view for selecting and adding new tags to an instance that mixes in Footprint.Tags + * @type {*} + */ +Footprint.CategoriesSelectView = Footprint.LabelSelectView.extend({ + classNames: "footprint-label-select-view".w(), + itemTitleKey: 'value', + showCheckbox: YES +}); diff --git a/sproutcore/apps/fp/views/clear_button_view.js b/sproutcore/apps/fp/views/clear_button_view.js new file mode 100644 index 000000000..3a811db99 --- /dev/null +++ b/sproutcore/apps/fp/views/clear_button_view.js @@ -0,0 +1,23 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.ClearButtonView = SC.ImageButtonView.extend(SC.ActionSupport, { + classNames:'footprint-info-clear-button-view'.w(), + // Set this + action: null, + image: 'clear-icon', + mouseUp: function(evt) { + this.fireAction(this.get('action')); + } +}); diff --git a/sproutcore/apps/fp/views/config_entity/analytic_bar_view.js b/sproutcore/apps/fp/views/config_entity/analytic_bar_view.js new file mode 100644 index 000000000..681a9d1b5 --- /dev/null +++ b/sproutcore/apps/fp/views/config_entity/analytic_bar_view.js @@ -0,0 +1,173 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.AnalyticBarView = SC.View.extend({ + childViews:'barView labelView'.w(), + layout: { height:20 }, // Sensible default + classNames: "footprint-analytic-bar-view".w(), + + /** + * The specific Result to use for this bar. If not specified the first result with configuration.result_type=='analytic_bars' will be used + */ + dbEntityKey:null, + + /** + * The specific query attribute key of the Result to show + */ + queryAttributeKey:null, + + /*** + * The configEntity from which we get the results + */ + configEntity:null, + configEntityStatus:null, + configEntityStatusBinding: SC.Binding.oneWay('*configEntity.status'), + presentations:null, + presentationsBinding:SC.Binding.oneWay('*configEntity.presentations.results'), + presentationsStatus:null, + presentationsStatusBinding:SC.Binding.oneWay('*presentations.status'), + /** + * The Results of a LayerLibrary instance. This is bound to the resultsController content + */ + content:function() { + if (this.get('presentations') && (this.getPath('presentationsStatus') & SC.Record.READY)) { + var libraries = this.get('presentations').filter(function(presentation, i) { + return presentation.get('key') == 'result_library__default'; + }, this); + if (libraries.get('length') > 0) { + return libraries[0].get('results'); + } + else { + // A bad clone or something means the config_entity doesn't have the default result library + logWarning("No default result library found for config_entity: %@".fmt(this.getPath('config_enity.name'))) + } + } + }.property('presentations', 'presentationsStatus').cacheable(), + contentStatus: null, + contentStatusBinding: SC.Binding.oneWay('*content.status'), + + /*** + * The result matching the dbEntityKey + */ + result: function() { + // Find the Result matching dbEntityKey or the first result of result_type 'analytic_bars' + if (this.get('content') && this.get('contentStatus') & SC.Record.READY) + return this.get('content').filter(function(result) { + return this.get('dbEntityKey') ? + result.get('db_entity_key') == this.get('dbEntityKey') : + result.getPath('configuration.result_type') == 'analytic_bars'; + + }, this)[0]; + }.property('content', 'contentStatus', 'dbEntityKey').cacheable(), + resultStatus:null, + resultStatusBinding:SC.Binding.oneWay('*result.status'), + + overallMinimum:null, + overallMaximum:null, + + /*** + * The minimum value of the bar + */ + minimum:function() { + if (this.get('value')) { + var num = Math.round(this.get('value') || 0); + var digits = num.toString().length-1; + return Math.pow(10,digits); + } + if (this.getPath('result.status') & SC.Record.READY) + return this.getPath('result.configuration.extent_lookup.%@.min'.fmt(this.get('queryAttributeKey'))); + }.property('result', 'resultStatus', 'queryAttributeKey', 'value').cacheable(), + + /*** + * The maximum value of the bar + */ + maximum:function() { + if (this.get('value')) { + var num = Math.round(this.get('value') || 0); + var digits = num.toString().length-1; + return Math.pow(10,digits+1); + } + if (this.getPath('result.status') & SC.Record.READY) + return this.getPath('result.configuration.extent_lookup.%@.max'.fmt(this.get('queryAttributeKey'))); + }.property('result', 'resultStatus', 'queryAttributeKey', 'value').cacheable(), + + /*** + * Returns a dict that maps result query column names to generalized attributes. This allows the table columns + * to have non-standard names, but our SC attributes/properties can be standardized + * (e.g. dwelling_units:households__sum) + * @returns {*} + * @private + */ + attributeToColumn: function() { + if (this.get('resultStatus') & SC.Record.READY) + return this.getPath('result.configuration.attribute_to_column'); + }.property('result', 'resultStatus').cacheable(), + + dbColumn:function() { + if (this.get('attributeToColumn') && this.get('queryAttributeKey')) + return this.get('attributeToColumn')[this.get('queryAttributeKey')] + }.property('attributeToColumn', 'queryAttributeKey').cacheable(), + + queryLookup:function() { + if (this.get('result') && (this.get('resultStatus') & SC.Record.READY)) + return $.mapObjectToObject( + this.getPath('result.query'), + function(key, value) { + // Remove the __sum or part so we match our attribute + return [key.split('__')[0], value] + }); + }.property('result', 'resultStatus').cacheable(), + + /*** + * The value of the query attribute based on the queryAttributeKey + */ + value: function() { + if (this.get('queryLookup') && this.get('dbColumn')) + var num = this.get('queryLookup')[this.get('dbColumn')]; + try { + return Number(num); + } + catch(Error) { + return null; + } + }.property('queryLookup', 'dbColumn').cacheable(), + + barView: SC.ProgressView.extend({ + // Normalizing this because setting maximum and minimum doesn't seem to work + min:null, + minBinding:SC.Binding.oneWay('.parentView.minimum'), + max:null, + maxBinding:SC.Binding.oneWay('.parentView.maximum'), + val:null, + valBinding:SC.Binding.oneWay('.parentView.value'), + value: function() { + return this.get('val') ? this.get('val') / (this.get('max') - this.get('min')) : 0; + }.property('minimum', 'maximum', 'val').cacheable() + }), + + labelView: SC.LabelView.extend({ + rawValue:null, + rawValueBinding:SC.Binding.oneWay('.parentView.value'), + value: function() { + var value = this.get('rawValue'); + return value ? d3.format(',f')(value) : 'N/A'; + }.property('rawValue').cacheable() + }), + + toString: function() { + return this.toStringAttributes('minimum maximum configEntity content dbEntityKey queryAttributeKey result value attributeToColumn'.w()); + } +}); + + diff --git a/sproutcore/apps/fp/views/copy_button_view.js b/sproutcore/apps/fp/views/copy_button_view.js new file mode 100644 index 000000000..a729a1353 --- /dev/null +++ b/sproutcore/apps/fp/views/copy_button_view.js @@ -0,0 +1,27 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.CopyButtonView = SC.ImageButtonView.extend(SC.ActionSupport, { + classNames:'footprint-info-add-button-view'.w(), + // Set this + action: null, + image: 'add-icon', + // recordType is optionally set in order to pass in the action context + recordType: null, + // content is optionally set in order to pass in the action context + content: null, + mouseUp: function(evt) { + this.fireAction(this.get('action')); + } +}); diff --git a/sproutcore/apps/fp/views/delete_button_view.js b/sproutcore/apps/fp/views/delete_button_view.js new file mode 100644 index 000000000..a7e680d75 --- /dev/null +++ b/sproutcore/apps/fp/views/delete_button_view.js @@ -0,0 +1,23 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.DeleteButtonView = SC.ImageButtonView.extend(SC.ActionSupport, { + classNames:'footprint-info-delete-button-view'.w(), + // Set this + action: null, + image: 'delete-icon', + mouseUp: function(evt) { + this.fireAction(this.get('action')); + } +}); diff --git a/sproutcore/apps/fp/views/info_views/analytics/analytic_module_views.js b/sproutcore/apps/fp/views/info_views/analytics/analytic_module_views.js new file mode 100644 index 000000000..64966eaa8 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/analytics/analytic_module_views.js @@ -0,0 +1,53 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 1/13/14 + * Time: 3:03 PM + * To change this template use File | Settings | File Templates. + */ + + +Footprint.ManageModuleView = SC.View.extend({ + classNames: "footprint-manage-module-view".w(), + childViews: ['titleView', 'executeModuleView', 'editPolicyView', 'infoButtonView'], + layout: {height: 120}, + title: null, + titleBinding: SC.Binding.oneWay('.parentView.title'), + executeModuleAction: null, + executeModuleActionBinding: SC.Binding.oneWay('.parentView.executeModuleAction'), + editAssumptionsAction: null, + editAssumptionsActionBinding: SC.Binding.oneWay('.parentView.editAssumptionsAction'), + + titleView: SC.LabelView.extend({ + classNames: "footprint-analytic-module-title-view".w(), + layout: {top: 15, left: 40, right: 40, height: 24}, + valueBinding: SC.Binding.oneWay('.parentView.title'), + textAlign: SC.ALIGN_CENTER + }), + + infoButtonView: SC.ButtonView.extend({ + classNames: "footprint-info-button-view".w(), + layout: {left: 10, height: 24, width: 24, top: 13}, + childViews: ['iconView'], + menuItems: function () { + return this.get('defaultMenuItems'); + }.property('defaultMenuItems').cacheable(), + defaultMenuItems: '--', + iconView: SC.ImageView.extend({ + layout: {left: 2, width: 20, height: 20, top: 1}, + value: sc_static('fp:images/info_logo.png') + }) + }), + + executeModuleView: SC.ButtonView.extend({ + layout: { left: 40, right: 40, height: 24, border: 1, top:55, width: 200}, + title: 'Run Module', + actionBinding: SC.Binding.oneWay('.parentView.executeModuleAction') + }), + + editPolicyView: SC.ButtonView.extend({ + layout: { left: 40, right: 40, height: 24, border: 1, top:80, width: 200}, + title: 'Edit Assumptions', + actionBinding: SC.Binding.oneWay('.parentView.editAssumptionsAction') + }) +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/analytics/documentation_info_pane.js b/sproutcore/apps/fp/views/info_views/analytics/documentation_info_pane.js new file mode 100644 index 000000000..e32c8e07f --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/analytics/documentation_info_pane.js @@ -0,0 +1,7 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 1/10/14 + * Time: 3:23 PM + * To change this template use File | Settings | File Templates. + */ diff --git a/sproutcore/apps/fp/views/info_views/analytics/fiscal_module_management_view.js b/sproutcore/apps/fp/views/info_views/analytics/fiscal_module_management_view.js new file mode 100644 index 000000000..2463c2c29 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/analytics/fiscal_module_management_view.js @@ -0,0 +1,80 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 11/15/13 + * Time: 11:56 AM + * To change this template use File | Settings | File Templates. + */ +sc_require('views/info_views/analytics/label_result_views'); +sc_require('views/info_views/analytics/analytic_module_views'); +sc_require('views/menu_render_mixin'); + +Footprint.FiscalModuleManagementView = SC.View.extend({ + + classNames: "footprint-fiscal-module-management-view".w(), + childViews: ['manageModuleView', 'moduleResultsView'], + + allResultsStatus: null, + allResults: null, + + title: 'Fiscal Module', + executeModuleAction: 'doRunFiscal', + editAssumptionsAction: 'doEditFiscalPolicy', + + allResultsBinding: SC.Binding.oneWay('Footprint.resultsController.content'), + allResultsStatusBinding: SC.Binding.oneWay('Footprint.resultsController.status'), + + content: function () { + if (this.get('allResultsStatus') & SC.Record.READY) + return this.get('allResults').filter(function (result) { + return result.getPath('db_entity_key') == 'result__fiscal'; + }); + }.property('allResults', 'allResultsStatus').cacheable(), + + contentFirstObject: null, + contentFirstObjectBinding : SC.Binding.oneWay('*content.firstObject'), + + manageModuleView: Footprint.ManageModuleView, + + moduleResultsView: SC.View.extend({ + classNames: "footprint-module-results-view".w(), + childViews: ['scenarioTitleView', 'capitalCostResultView', 'operationsResultView', 'revenueResultView'], + layout: {top: 120}, + + content: null, + contentBinding: SC.Binding.oneWay('.parentView.contentFirstObject'), + + scenarioTitleView: SC.LabelView.extend({ + layout: {top: 10, left: 10, right: 10, height: 24}, + scenarioName: null, + scenarioNameBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.name'), + value: function() { + return '%@:'.fmt(this.get('scenarioName')); + }.property('scenarioName') + }), + + capitalCostResultView: Footprint.TopLabeledResultView.extend({ + layout: {height: 40, top: 40, left: 40, right: 40, width: 200}, + result: null, + resultBinding: SC.Binding.oneWay('.parentView*content.query'), + columnName: 'residential_capital_costs__sum', + title: 'Capital Costs ($)' + }), + + operationsResultView: Footprint.TopLabeledResultView.extend({ + layout: {height: 40, top: 90, left: 40, right: 40, width: 200}, + result: null, + resultBinding: SC.Binding.oneWay('.parentView*content.query'), + columnName: 'residential_operations_maintenance_costs__sum', + title: 'Operations/Maintenance Costs ($)' + }), + + revenueResultView: Footprint.TopLabeledResultView.extend({ + layout: {height: 40, top: 140, left: 40, right: 40, width: 200}, + result: null, + resultBinding: SC.Binding.oneWay('.parentView*content.query'), + columnName: 'residential_revenue__sum', + title: 'Revenue ($)' + }) + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/analytics/label_result_views.js b/sproutcore/apps/fp/views/info_views/analytics/label_result_views.js new file mode 100644 index 000000000..cd33b9380 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/analytics/label_result_views.js @@ -0,0 +1,53 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 1/9/14 + * Time: 4:34 PM + * To change this template use File | Settings | File Templates. + */ + +Footprint.TopLabeledResultView = SC.View.extend({ + classNames: "footprint-top-labeled-result-view".w(), + childViews: ['titleSpaceView', 'valueSpaceView'], + result: null, + title: null, + columnName: null, + + columnValue: function() { + var content = this.get('result') + var column_name = this.get('columnName') + if (!content) + return '--'; + var value = parseFloat(content[column_name]).toFixed(0) + + return value ? d3.format(',f')(value) : '--'; + }.property('result', 'columnName').cacheable(), + + titleSpaceView: SC.View.extend({ + classNames: "footprint-top-labeled-result-title-view".w(), + childViews: ['titleView'], + title:null, + titleBinding: SC.Binding.oneWay('.parentView.title'), + layout: {height: 0.5}, + + titleView: SC.LabelView.extend({ + classNames: "footprint-top-labeled-result-title-space-view".w(), + layout: {top:2, bottom: 2, left:5}, + valueBinding: SC.Binding.oneWay('.parentView.title') + }) + }), + + valueSpaceView: SC.View.extend({ + classNames: "footprint-top-labeled-result-value-space-view".w(), + childViews: ['valueView'], + layout: {top: 0.5}, + columnValue: null, + columnValueBinding: SC.Binding.oneWay('.parentView.columnValue'), + valueView: SC.LabelView.extend({ + classNames: "footprint-top-labeled-result-value-view".w(), + layout: {top: 4}, + valueBinding: SC.Binding.oneWay('.parentView.columnValue'), + textAlign: SC.ALIGN_CENTER + }) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/analytics/vmt_module_management_view.js b/sproutcore/apps/fp/views/info_views/analytics/vmt_module_management_view.js new file mode 100644 index 000000000..773a5e352 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/analytics/vmt_module_management_view.js @@ -0,0 +1,96 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 11/15/13 + * Time: 11:56 AM + * To change this template use File | Settings | File Templates. + */ +sc_require('views/info_views/analytics/label_result_views'); +sc_require('views/info_views/analytics/analytic_module_views'); +sc_require('views/menu_render_mixin'); + +Footprint.VmtModuleManagementView = SC.View.extend({ + + classNames: "footprint-vmt-module-management-view".w(), + childViews: ['manageModuleView', 'moduleResultsView'], + + allResultsStatus: null, + allResults: null, + + title: 'VMT Module', + executeModuleAction: 'doRunVMT', + editAssumptionsAction: 'doEditVMTPolicy', + + allResultsBinding: SC.Binding.oneWay('Footprint.resultsController.content'), + allResultsStatusBinding: SC.Binding.oneWay('Footprint.resultsController.status'), + + content: function () { + if (this.get('allResultsStatus') & SC.Record.READY) + return this.get('allResults').filter(function (result) { + return result.getPath('db_entity_key') == 'result__vmt'; + }); + }.property('allResults', 'allResultsStatus').cacheable(), + + contentFirstObject: null, + contentFirstObjectBinding : SC.Binding.oneWay('*content.firstObject'), + + manageModuleView: Footprint.ManageModuleView, + + moduleResultsView: SC.View.extend({ + classNames: "footprint-module-results-view".w(), + childViews: ['scenarioTitleView', 'dailyVmtResultView', 'annualVmtResultView', 'dailyPerHhVmtResultView', 'annualPerCapitaVmtResultView', 'dailyPerEmployeeVmtResultView'], + layout: {top: 120}, + + content: null, + contentBinding: SC.Binding.oneWay('.parentView.contentFirstObject'), + + scenarioTitleView: SC.LabelView.extend({ + layout: {top: 10, left: 10, right: 10, height: 24}, + scenarioName: null, + scenarioNameBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.name'), + value: function() { + return '%@:'.fmt(this.get('scenarioName')); + }.property('scenarioName') + }), + + dailyVmtResultView: Footprint.TopLabeledResultView.extend({ + layout: {height: 40, top: 40, left: 40, right: 40, width: 200}, + result: null, + resultBinding: SC.Binding.oneWay('.parentView*content.query'), + columnName: 'daily_vmt__sum', + title: 'Total Daily VMT' + }), + + annualVmtResultView: Footprint.TopLabeledResultView.extend({ + layout: {height: 40, top: 90, left: 40, right: 40, width: 200}, + result: null, + resultBinding: SC.Binding.oneWay('.parentView*content.query'), + columnName: 'annual_vmt__sum', + title: 'Total Annual VMT' + }), + + dailyPerHhVmtResultView: Footprint.TopLabeledResultView.extend({ + layout: {height: 40, top: 140, left: 40, right: 40, width: 200}, + result: null, + resultBinding: SC.Binding.oneWay('.parentView*content.query'), + columnName: 'daily_vmt_per_hh__sum', + title: 'Daily VMT Per HH' + }), + + annualPerCapitaVmtResultView: Footprint.TopLabeledResultView.extend({ + layout: {height: 40, top: 190, left: 40, right: 40, width: 200}, + result: null, + resultBinding: SC.Binding.oneWay('.parentView*content.query'), + columnName: 'annual_vmt_per_capita__sum', + title: 'Annual VMT Per Capita' + }), + + dailyPerEmployeeVmtResultView: Footprint.TopLabeledResultView.extend({ + layout: {height: 40, top: 240, left: 40, right: 40, width: 200}, + result: null, + resultBinding: SC.Binding.oneWay('.parentView*content.query'), + columnName: 'daily_vmt_per_emp__sum', + title: 'Daily VMT Per Employee' + }) + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/built_form_color_picker_view.js b/sproutcore/apps/fp/views/info_views/built_form/built_form_color_picker_view.js new file mode 100644 index 000000000..7533277e2 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/built_form_color_picker_view.js @@ -0,0 +1,145 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/16/13 + * Time: 2:11 PM + * To change this template use File | Settings | File Templates. + */ + + +Footprint.BuiltFormColorPickerView = SC.View.design({ + childViews: ['colorPreviewCV', 'colorPreview1CV', 'colorPreview2CV', 'hslSlidersCV', 'cssTextCV'], + classNames: ['demo-content'], + layout: { left: 30, width: 600, height: 260, top: 250 }, + selection: null, + + color: null, + colorBinding: SC.Binding.oneWay('*selection.medium').transform(function(medium) { + if (medium) + return medium.getPath('content.fill.color'); + return '#4682B4' + }), + + colorPreviewCV: SC.View.design({ + classNames: ['color-preview'], + backgroundColorBinding: SC.Binding.oneWay('.parentView.color'), + + displayProperties: ['backgroundColor'], + + layout: { border: 1, left: 15, top: 15, width: 170, height: 120 } + }), + + colorPreview1CV: SC.ImageButtonView.design({ + action: function() { + var brighterColor = Footprint.mainViewController.get('brighterColor'); + + Footprint.mainViewController.set('hue', brighterColor.get('hue')); + Footprint.mainViewController.set('saturation', brighterColor.get('saturation')); + Footprint.mainViewController.set('luminosity', brighterColor.get('luminosity')); + }, + backgroundColorBinding: SC.Binding.oneWay('Footprint.mainViewController.brighterCssText'), + classNames: ['color-sub-preview'], + layout: { border: 2, left: 20, top: 20, width: 20, height: 20 } + }), + + colorPreview2CV: SC.ImageButtonView.design({ + action: function() { + var darkerColor = Footprint.mainViewController.get('darkerColor'); + + Footprint.mainViewController.set('hue', darkerColor.get('hue')); + Footprint.mainViewController.set('saturation', darkerColor.get('saturation')); + Footprint.mainViewController.set('luminosity', darkerColor.get('luminosity')); + }, + backgroundColorBinding: SC.Binding.oneWay('Footprint.mainViewController.darkerCssText'), + classNames: ['color-sub-preview'], + layout: { border: 2, left: 158, top:105, width: 20, height: 20 } + }), + + hslSlidersCV: SC.View.design({ + childViews: ['hTitle', 'hCV', 'hValueCV', 'sTitle', 'sCV', 'sValueCV', 'lTitle', 'lCV', 'lValueCV'], + layout: { top: 10, right: 15, left:190, height:130 }, + + hTitle: SC.LabelView.design({ + classNames: ['slider-title'], + layout: { left: 10, width: 20, height: 24, top: 0 }, + localize: true, + value: 'H' + }), + hCV: SC.SliderView.design({ + layout: { left: 50, right: 50, height: 24, top: 0 }, + minimum: 0, + maximum: 359, + step: 1, + valueBinding: SC.Binding.from('Footprint.mainViewController.hue') + }), + hValueCV: SC.LabelView.design({ + classNames: ['slider-value'], + layout: { right: 10, width: 35, height: 24, top: 0 }, + valueBinding: SC.Binding.oneWay('Footprint.mainViewController.hue'). + transform(function(hue) { + return parseInt(hue, 10) + '°'; + }) + }), + + sTitle: SC.LabelView.design({ + classNames: ['slider-title'], + layout: { left: 10, width: 20, height: 24, top: 35 }, + localize: true, + value: 'S' + }), + sCV: SC.SliderView.design({ + layout: { left: 50, right: 50, height: 24, top: 35 }, + step: 0.01, + valueBinding: SC.Binding.from('Footprint.mainViewController.saturation') + }), + sValueCV: SC.LabelView.design({ + classNames: ['slider-value'], + layout: { right: 10, width: 35, height: 24, top: 35 }, + valueBinding: SC.Binding.oneWay('Footprint.mainViewController.saturation'). + transform(function(saturation) { + return parseInt(saturation * 100, 10) + '%'; + }) + }), + + lTitle: SC.LabelView.design({ + classNames: ['slider-title'], + layout: { left: 10, width: 20, height: 24, top: 70 }, + localize: true, + value: 'L' + }), + lCV: SC.SliderView.design({ + layout: { left: 50, right: 50, height: 24, top: 70 }, + step: 0.01, + valueBinding: SC.Binding.from('Footprint.mainViewController.luminosity') + }), + lValueCV: SC.LabelView.design({ + classNames: ['slider-value'], + layout: { right: 10, width: 35, height: 24, top: 70 }, + valueBinding: SC.Binding.oneWay('Footprint.mainViewController.luminosity'). + transform(function(luminosity) { + return parseInt(luminosity * 100, 10) + '%'; + }) + }) + }), + + cssTextCV: SC.TextFieldView.design({ + classNames: ['color-text'], + controlSize: SC.LARGE_CONTROL_SIZE, + isEditable: YES, + layout: { left: 100, right: 100, bottom: 75, height: 30 }, + +// parentColor: null, +// parentColorBinding: SC.Binding.oneWay('.parentView.color'), +// +// newColor: null, +// newColorBinding: SC.Binding.oneWay('Footprint.mainViewController.cssText'), +// value: function(){ +// var value = this.get('parentColor'); +// if (value) +// return value; +// return this.get('newColor') +// }.property('parentColor').cacheable() + + valueBinding: SC.Binding.oneWay('Footprint.mainViewController.cssText') + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/built_form_info_pane.js b/sproutcore/apps/fp/views/info_views/built_form/built_form_info_pane.js new file mode 100644 index 000000000..8a8476a5d --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/built_form_info_pane.js @@ -0,0 +1,47 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/9/13 + * Time: 10:08 AM + * To change this template use File | Settings | File Templates. + */ + +sc_require('views/info_views/built_form/manage_building_view'); +sc_require('views/info_views/built_form/manage_building_type_view'); +sc_require('views/info_views/built_form/manage_placetype_view'); + + +Footprint.BuiltFormInfoPane = SC.PalettePane.extend(SC.ActionSupport,{ + classNames: ['footprint-add-building-pane'], + layout: { top: 100, width: 1250, right: 280, height: 650 }, + nowShowing:function() { + return this.getPath('context.nowShowing') || 'Footprint.ManageBuildingView'; + }.property('context').cacheable(), + contentView: SC.ContainerView.design({ + classNames: ['footprint-add-built-form-container-view'], + childViews: ['copyButtonView', 'deleteButtonView', 'buttonsView'], + + recordType: null, + // The recordType is stored in the contentView of this view (the nowShowing view) + recordTypeBinding: SC.Binding.oneWay('*contentView.recordType'), + // Likewise for the current selectedItem + selectedItemBinding: SC.Binding.oneWay('*contentView.selectedItem'), + + + buttonsView: SC.SegmentedView.extend({ + layout: {top: 5, centerY:0, height:22}, + crudType: 'view', + itemTitleKey: 'title', + itemActionKey: 'action', + items: [ + {title: 'Building', action: 'doManageBuildings'}, + {title: 'Building Type', action: 'doManageBuildingTypes'}, + {title: 'Placetype', action: 'doManagePlaceTypes'} + ] + }), + nowShowingBinding: SC.Binding.oneWay('.parentView.nowShowing') + }) +}) + + + diff --git a/sproutcore/apps/fp/views/info_views/built_form/built_form_items_scroll_view.js b/sproutcore/apps/fp/views/info_views/built_form/built_form_items_scroll_view.js new file mode 100644 index 000000000..d0f9ee020 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/built_form_items_scroll_view.js @@ -0,0 +1,107 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/11/13 + * Time: 5:19 PM + * To change this template use File | Settings | File Templates. + */ + + +Footprint.BuiltFormItemsScrollView = SC.View.extend({ + + childViews: ['overlayView', 'copyButtonView', 'deleteButtonView', 'scrollView'], + + media: null, + content: null, + selection: null, + // recordType is used for cloning + recordType: null, + + overlayView: Footprint.OverlayView.extend({ + contentBinding: SC.Binding.from('.parentView.content'), + statusBinding:SC.Binding.oneWay('*content.status') + }), + + copyButtonView: Footprint.CopyButtonView.extend({ + layout: { left: 0, width: 16, top: 0, height: 16 }, + action: 'doCloneRecord', + // We want to clone the selected item + contentBinding: SC.Binding.oneWay('.parentView*selection.firstObject'), + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + isVisibleBinding: SC.Binding.oneWay('.content').bool() + }), + + deleteButtonView: Footprint.DeleteButtonView.extend({ + layout: { left: 24, width: 16, top: 0, height: 16}, + action: 'doPromptDeleteRecord', + // We want to delete the selected item + contentBinding: SC.Binding.oneWay('.parentView*selection.firstObject'), + isVisibleBinding: SC.Binding.oneWay('.content').bool() + }), + + scrollView: SC.ScrollView.extend({ + layout: { top: 20}, + classNames: ['footprint-built-form-scroll-view'], + + media: null, + mediaBinding: SC.Binding.oneWay('.parentView.media'), + + content: null, + contentBinding:SC.Binding.oneWay('.parentView.content'), + + selection: null, + selectionBinding: SC.Binding.from('.parentView.selection'), + + contentView: SC.SourceListView.extend({ + isEnabledBinding: SC.Binding.oneWay('.content').bool(), + rowHeight: 20, + actOnSelect: NO, + contentIconKey: 'medium', + contentValueKey: 'name', + contentBinding: SC.Binding.oneWay('.parentView.parentView.content'), + selectionBinding: SC.Binding.from('.parentView.parentView.selection'), + + exampleView: SC.View.extend(SC.Control, SC.ContentDisplay, { + classNames: ['footprint-built-form-item'], + contentDisplayProperties: ['name'], + classNameBindings: ['isNew:new-record', 'isDirty:dirty-record'], + childViews:['progressOverlayView'], + + status: null, + statusBinding: SC.Binding.oneWay('.content.status'), + isNew: function() { + return this.get('status') === SC.Record.READY_NEW; + }.property('status').cacheable(), + + isDirty: function() { + return this.get('status') === SC.Record.READY_DIRTY; + }.property('status').cacheable(), + + progressOverlayView: Footprint.ProgressOverlayView.extend({ + layout: { left:.5, width:.5, centerY: 0, height: 16}, + contentBinding: SC.Binding.oneWay('.parentView.content') + }), + + render: function(context) { + // Color swab + var color = this.getPath('content.medium.content.fill.color'); + context.begin() + .addClass(this.getPath('theme.classNames')) + .addClass(['sc-view', 'footprint-medium-color']) + .setStyle({ 'background-color': color }) + .end(); + // Label + context.begin() + .addClass(this.getPath('theme.classNames')) + .addClass(['sc-view', 'sc-label-view', 'footprint-built-form-item-label-view']) + .push(this.getPath('content.name')) + .end(); + }, + update: function ($context) { + $context.find('.footprint-medium-color').css('background-color', this.getPath('content.medium.content.fill.color')); + $context.find('.footprint-built-form-item-label-view').text(this.getPath('content.name')); + } + }) + }) + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/built_form_set_info_view.js b/sproutcore/apps/fp/views/info_views/built_form/built_form_set_info_view.js new file mode 100644 index 000000000..52b1c1b8e --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/built_form_set_info_view.js @@ -0,0 +1,35 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/geography_info_view'); +sc_require('views/info_views/tags_info_view'); +sc_require('views/info_views/medium_info_view'); +sc_require('views/info_views/key_info_view'); +sc_require('models/built_form_models'); + +/*** + * The pane used to edit the settings of a create or existing BuiltFormSet and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the BuiltFormSet if a DbEntity is being created here + * @type {*} + */ +Footprint.BuiltFormSetInfoView = Footprint.InfoPane.extend({ + layout: { top:0, left: 0, width:400, height:400 }, + classNames: "footprint-built-form-set-info-view".w(), + + recordType: Footprint.BuiltFormSet, + + contentView: Footprint.InfoView.extend({ + // TODO look at git history and redo this + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/built_form/built_form_summary_attributes_view.js b/sproutcore/apps/fp/views/info_views/built_form/built_form_summary_attributes_view.js new file mode 100644 index 000000000..409f612ba --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/built_form_summary_attributes_view.js @@ -0,0 +1,97 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/3/13 + * Time: 5:43 PM + * To change this template use File | Settings | File Templates. + */ +sc_require('views/info_views/built_form/editable_input_field_view'); + +Footprint.BuiltFormSummaryAttributesView = SC.View.extend({ + classNames: ['footprint-built-form-summary-attributes-view'], + childViews:'titleView dwellingUnitView singleFamilyLargeLotView singleFamilySmallLotView attachedSingleFamilyView multifamilyView employmentView retailEmploymentView officeEmploymentView industrialEmploymentView agriculturalEmploymentView'.w(), + layout: {left: 970, height: 0.6}, + content: null, + + titleView: SC.LabelView.extend({ + layout: {top: 20, left: 10, width: 260, height:18}, + value: 'Summary Densities (per acre)' + }), + + dwellingUnitView: Footprint.summaryFieldView.extend({ + layout: {top: 50, left: 10, width: 260, height:18}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.dwelling_unit_density'), + value: 'Dwelling Unit Density', + titleTextAlignment: SC.ALIGN_LEFT + }), + + singleFamilyLargeLotView: Footprint.summaryFieldView.extend({ + layout: {top: 75, left: 10, width: 260, height:18}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.single_family_large_lot_density'), + value: 'SF Large Lot Density', + titleTextAlignment: SC.ALIGN_CENTER + }), + + singleFamilySmallLotView: Footprint.summaryFieldView.extend({ + layout: {top: 100, left: 10, width: 260, height:18}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.single_family_small_lot_density'), + value: 'SF Small Lot Density', + titleTextAlignment: SC.ALIGN_CENTER + }), + + attachedSingleFamilyView: Footprint.summaryFieldView.extend({ + layout: {top: 125, left: 10, width: 260, height:18}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.attached_single_family_density'), + value: 'Attached SF Density', + titleTextAlignment: SC.ALIGN_CENTER + }), + + multifamilyView: Footprint.summaryFieldView.extend({ + layout: {top: 150, left: 10, width: 260, height:18}, + mf2to4: null, + mf2to4Binding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.multifamily_2_to_4_density'), + mf5plus: null, + mf5plusBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.multifamily_5_plus_density'), + + content: function() { + return this.get('mf5plus') + this.get('mf2to4')}.property('mf5plus', 'mf2to4').cacheable(), + + value: 'Multifamily Density', + titleTextAlignment: SC.ALIGN_CENTER + }), + + employmentView: Footprint.summaryFieldView.extend({ + layout: {top: 200, left: 10, width: 260, height:18}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.employment_density'), + value: 'Employment Density', + titleTextAlignment: SC.ALIGN_LEFT + }), + + retailEmploymentView: Footprint.summaryFieldView.extend({ + layout: {top: 225, left: 10, width: 260, height:18}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.retail_density'), + value: 'Retail Density', + titleTextAlignment: SC.ALIGN_CENTER + }), + + officeEmploymentView: Footprint.summaryFieldView.extend({ + layout: {top: 250, left: 10, width: 260, height:18}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.office_density'), + value: 'Office Density', + titleTextAlignment: SC.ALIGN_CENTER + }), + + industrialEmploymentView: Footprint.summaryFieldView.extend({ + layout: {top: 275, left: 10, width: 260, height:18}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.industrial_density'), + value: 'Industrial Density', + titleTextAlignment: SC.ALIGN_CENTER + }), + + agriculturalEmploymentView: Footprint.summaryFieldView.extend({ + layout: {top: 300, left: 10, width: 260, height:18}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.flat_building_densities.agricultural_density'), + value: 'Agriculture Density', + titleTextAlignment: SC.ALIGN_CENTER + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/editable_building_attributes_view.js b/sproutcore/apps/fp/views/info_views/built_form/editable_building_attributes_view.js new file mode 100644 index 000000000..9bc7f6fd2 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/editable_building_attributes_view.js @@ -0,0 +1,76 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 11/26/13 + * Time: 3:07 PM + * To change this template use File | Settings | File Templates. + */ +sc_require('views/info_views/built_form/editable_input_field_view'); +sc_require('views/info_views/built_form/editable_building_use_type_view'); + +Footprint.EditableBuildingAttributesView = SC.View.extend({ + classNames: ['footprint-building-input-view'], + childViews:'buildingStoriesView totalFarView parkingSpacesView parkingSqFtView hardscapePercentView irrigatedPercentView imperviousPercentView vacancyRateView householdSizeView residentialLotSqFtView editableBuildingUseTypeView'.w(), + + content: null, + + layout: {left: 330, bottom:40, top: 340, width: 650}, + + buildingStoriesView: Footprint.editableInputFieldView.extend({ + layout: {top: 10, left: 30, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.floors'), + value: 'Building Stories' + }), + totalFarView: Footprint.editableInputFieldView.extend({ + layout: {top: 40, left: 30, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.total_far'), + value: 'Total FAR' + }), + parkingSpacesView: Footprint.editableInputFieldView.extend({ + layout: {top: 70, left: 30, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.parking_spaces'), + value: 'Parking Spaces' + }), + parkingSqFtView: Footprint.editableInputFieldView.extend({ + layout: {top: 100, left: 30, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.parking_structure_square_feet'), + value: 'Parking Structure SqFt' + }), + hardscapePercentView: Footprint.editableInputFieldView.extend({ + layout: {top: 130, left: 30, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.hardscape_percent'), + value: 'Hardscape Percent' + }), + irrigatedPercentView: Footprint.editableInputFieldView.extend({ + layout: {top: 160, left: 30, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.irrigated_percent'), + value: 'Irrigated Percent' + }), + imperviousPercentView: Footprint.editableInputFieldView.extend({ + layout: {top: 190, left: 30, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.impervious_roof_percent'), + value: 'Impervious Use Percent' + }), + vacancyRateView: Footprint.editableInputFieldView.extend({ + layout: {top: 145, left: 320, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set').buildingUseFilter('vacancy_rate', 'Residential'), + value: 'Vacancy Rate' + }), + + householdSizeView: Footprint.editableInputFieldView.extend({ + layout: {top: 170, left: 320, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set').buildingUseFilter('household_size', 'Residential'), + value: 'Household Size' + }), + + residentialLotSqFtView: Footprint.editableInputFieldView.extend({ + layout: {top: 195, left: 320, width: 260, height:20}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set.residential_average_lot_size'), + value: 'SF Lot Square Feet' + }), + + editableBuildingUseTypeView: Footprint.editableBuildingUseTypeView.extend({ + layout: {left: 320, width: 320, height: 125, top:10}, + contentBinding: SC.Binding.oneWay('.parentView*content.building_attribute_set') + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/editable_building_type_attributes_view.js b/sproutcore/apps/fp/views/info_views/built_form/editable_building_type_attributes_view.js new file mode 100644 index 000000000..7fc037d06 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/editable_building_type_attributes_view.js @@ -0,0 +1,67 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/5/13 + * Time: 12:32 PM + * To change this template use File | Settings | File Templates. + */ +sc_require('views/info_views/built_form/editable_input_field_view'); +sc_require('views/info_views/built_form/built_form_color_picker_view'); + +Footprint.EditableBuildingTypeAttributesView = SC.View.extend({ + + classNames: ['footprint-editable-building-type-attributes-view'], + childViews:'titleView duAcreTitleView empAcreTitleView usePctTitleView buildingTypePercentScrollView BuiltFormColorPickerView'.w(), + layout: {bottom: 40, left: 340, right: 280, top:70}, + + titleView: SC.LabelView.extend({ + value: 'Included Buildings in Building Type', + layout: {left: 30, width: 250, height: 24, top: 0} + }), + + duAcreTitleView: SC.LabelView.extend({ + classNames: ['footprint-editable-building-type-attributes-du-label'], + layout: {width: 50, height: 22, left: 458, top: 10}, + value: 'Du/Acre' + }), + + empAcreTitleView: SC.LabelView.extend({ + classNames: ['footprint-editable-building-type-attributes-emp-label'], + layout: {width: 50, height: 22, left: 500, top: 10}, + value: 'Emp/Acre' + }), + + usePctTitleView: SC.LabelView.extend({ + classNames: ['footprint-editable-building-type-attributes-use-pct-label'], + layout: {width: 50, height: 22, left: 575, top: 10}, + value: 'Use Pct' + }), + + buildingTypePercentScrollView: SC.ScrollView.extend({ + classNames: ['footprint-building-type-percent-scroll-view'], + layout: {left: 40, width: 600, top: 25, height: 200}, + contentView: SC.SourceListView.extend({ + isEnabledBinding: SC.Binding.oneWay('.content').bool(), + rowHeight: 20, + isEditable: YES, + actOnSelect: NO, + canEditContent: YES, + canDeleteContent: YES, + canReorderContent: YES, + contentBinding: SC.Binding.oneWay('Footprint.buildingTypesEditController*selection.firstObject.primary_component_percents'), + selectionBinding: SC.Binding.from('Footprint.buildingTypesEditController.selection.firstObject'), + contentValueKey: 'name', + + exampleView: Footprint.editableBuiltFormSourceListView.extend({ + nameValueBinding:SC.Binding.oneWay('*content.primary_component.name'), + duValueBinding:SC.Binding.oneWay('*content.primary_component.building_attribute_set.flat_building_densities.dwelling_unit_density'), + empValueBinding:SC.Binding.oneWay('*content.primary_component.building_attribute_set.flat_building_densities.employment_density'), + percentValueBinding: '*content.percent' + }) + }) + }), + + BuiltFormColorPickerView: Footprint.BuiltFormColorPickerView.extend({ + selectionBinding: SC.Binding.oneWay('Footprint.buildingTypesEditController*selection.firstObject') + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/editable_building_types_tagged_buildings_view.js b/sproutcore/apps/fp/views/info_views/built_form/editable_building_types_tagged_buildings_view.js new file mode 100644 index 000000000..e636d03ea --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/editable_building_types_tagged_buildings_view.js @@ -0,0 +1,74 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/11/13 + * Time: 5:26 PM + * To change this template use File | Settings | File Templates. + */ + +sc_require('views/clear_button_view'); + + +Footprint.EditableBuildingTypesTaggedBuildingsView = SC.View.extend({ + layout: {left: 975, top: 0.55, bottom: 50}, + childViews:'titleView buildingTypeListView buildingTypeSelectButtonView'.w(), + + content:null, + selection: null, + + titleView: SC.LabelView.extend({ + value: 'Building Type Membership', + layout: {height: 20, top: 3}, + textAlign: SC.ALIGN_CENTER + }), + + buildingTypeListView:SC.ScrollView.extend({ + layout: { left:10, right:10, top: 23, bottom: 60}, + + contentBinding: SC.Binding.oneWay('.parentView*selection.firstObject'), + + contentView: SC.SourceListView.extend({ + isEnabledBinding: SC.Binding.oneWay('.content').bool(), + rowHeight: 34, + isEditable: YES, + actOnSelect: NO, + canEditContent: YES, + canDeleteContent: YES, + canReorderContent: YES, + + contentBinding: SC.Binding.oneWay('.parentView.parentView*content.placetype_components'), + contentValueKey: 'name', + selection: null, + + exampleView: SC.View.extend(SC.Control, { + classNames: ['footprint-building-type-member-example-view'], + layout: { height: 34 }, + displayProperties: 'content'.w(), + childViews: 'nameLabelView removeBuildingTypeButtonView'.w(), + content: null, + contentBinding: SC.Binding.oneWay('*content'), + + nameLabelView: SC.LabelView.extend({ + layout: { left: 0.02, top: 0.1, right: 24 }, + valueBinding: SC.Binding.oneWay('.parentView.content.name') + }), + removeBuildingTypeButtonView: Footprint.ClearButtonView.extend({ + isVisibleBinding: SC.Binding.oneWay('.parentView*content.name').bool(), + layout: {right: 0, width: 24}, + action: 'doRemoveBuildingType' + }) + }) + }) + }), + + buildingTypeSelectButtonView: Footprint.SelectInfoView.extend({ + classNames:'footprint-query-info-group-by-view'.w(), + layout: {bottom:25, left:.05, right: 0.05, height: 24}, + includeNullItem:YES, + nullTitle: 'None', + recordType: Footprint.PlacetypeComponent, + contentBinding: SC.Binding.oneWay('Footprint.buildingTypesEditController.content'), + selectionBinding: 'Footprint.buildingTypesEditController.selection', + itemTitleKey:'name' + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/editable_building_use_percents_view.js b/sproutcore/apps/fp/views/info_views/built_form/editable_building_use_percents_view.js new file mode 100644 index 000000000..2f9256d5e --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/editable_building_use_percents_view.js @@ -0,0 +1,224 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 11/26/13 + * Time: 4:08 PM + * To change this template use File | Settings | File Templates. + */ + +sc_require('views/info_views/built_form/editable_input_field_view'); + + +Footprint.EditableBuildingUsePercentView = SC.View.extend({ + classNames: ['footprint-editable-building-use-percents-view'], + childViews:'titleView buildingUsePercentScrollView'.w(), + layout: {left: 330, height:270, top:70, width: 650}, + content: null, + + titleView: SC.LabelView.extend({ + value: 'Building Use Percents', + layout: {left: 30, width: 150, height: 24, top: 0} + }), + + buildingUsePercentScrollView: SC.ScrollView.extend({ + classNames: ['footprint-building-use-percent-scroll-view'], + layout: {left: 40, width: 600, top: 25}, + contentBinding: SC.Binding.oneWay('.parentView*content'), + + contentView: SC.View.extend({ + layout: {height: 500}, + childViews:'residentialView retailView officeView industrialView'.w(), + isEditable: YES, + isEnabled: YES, + content:null, + contentBinding: SC.Binding.oneWay('.parentView*parentView.content.building_attribute_set.building_uses'), + + residentialView: SC.View.extend({ + childViews:'titleView singleFamilyLargeLotView singleFamilySmallLotView attachedSingleFamilyView multifamily2to4View multifamily5plusView'.w(), + content:null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + + layout: {height:111, top: 0}, + + titleView: SC.LabelView.extend({ + value: 'Residential', + textAlign: SC.ALIGN_LEFT, + layout: {left: 0, height:20, top: 0}, + backgroundColor: '#99CCFF' + }), + singleFamilyLargeLotView: Footprint.EditableBuildingUseFieldView.extend({ + title: 'Single Family Large Lot (> 5500 sqft per unit)', + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 21, height: 17}, + buildingUseProperty: 'percent', + category: 'Single Family Large Lot' + + }), + singleFamilySmallLotView:Footprint.EditableBuildingUseFieldView.extend({ + title: 'Single Family Small Lot (< 5500 sqft per unit)', + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 39, height: 17}, + buildingUseProperty: 'percent', + category: 'Single Family Small Lot' + + }), + attachedSingleFamilyView:Footprint.EditableBuildingUseFieldView.extend({ + title: 'Attached Single Family', + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 57, height: 17}, + buildingUseProperty: 'percent', + category: 'Attached Single Family' + }), + multifamily2to4View:Footprint.EditableBuildingUseFieldView.extend({ + title: 'Multifamily 2 to 4 Units', + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 75, height: 17}, + buildingUseProperty: 'percent', + category: 'Multifamily 2 To 4' + }), + multifamily5plusView:Footprint.EditableBuildingUseFieldView.extend({ + title: 'Multifamily 5 Plus Units', + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 93, height: 17}, + buildingUseProperty: 'percent', + category: 'Multifamily 5 Plus' + }) + }), + + retailView: SC.View.extend({ + childViews:'retailTitleView retailServicesView restaurantView accommodationView artsEntertainmentView otherServicesView'.w(), + content:null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {height:111, top: 111}, + + retailTitleView: SC.LabelView.extend({ + value: 'Retail Employment', + fontWeight: 700, + textAlign: SC.ALIGN_LEFT, + layout: {left: 0, height:20, top: 0}, + backgroundColor: '#99CCFF' + }), + retailServicesView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 21, height: 17}, + buildingUseProperty: 'percent', + category: 'Retail Services' + }), + restaurantView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 39, height: 17}, + buildingUseProperty: 'percent', + category: 'Restaurant' + }), + accommodationView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 57, height: 17}, + buildingUseProperty: 'percent', + category: 'Accommodation' + }), + artsEntertainmentView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 75, height: 17}, + buildingUseProperty: 'percent', + category: 'Arts Entertainment' + }), + otherServicesView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 93, height: 17}, + buildingUseProperty: 'percent', + category: 'Other Services' + }) + }), + officeView: SC.View.extend({ + childViews:'officeTitleView officeServicesView publicAdminView educationServicesView medicalServicesView'.w(), + content:null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {height:93, top: 222}, + + officeTitleView: SC.LabelView.extend({ + value: 'Office Employment', + fontWeight: 700, + textAlign: SC.ALIGN_LEFT, + layout: {left: 0, height:20, top: 0}, + backgroundColor: '#99CCFF' + }), + officeServicesView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 21, height: 17}, + buildingUseProperty: 'percent', + category: 'Office Services' + }), + publicAdminView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 39, height: 17}, + buildingUseProperty: 'percent', + category: 'Public Admin' + }), + + educationServicesView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 57, height: 17}, + buildingUseProperty: 'percent', + category: 'Education Services' + }), + + medicalServicesView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 75, height: 17}, + buildingUseProperty: 'percent', + category: 'Medical Services' + }) + }), + industrialView: SC.View.extend({ + childViews:'industrialTitleView manufacturingView wholesaleView transportWarehousingServicesView constructionUtilitiesView agricultureView extractionView'.w(), + content:null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {height:128, top: 315}, + + industrialTitleView: SC.LabelView.extend({ + value: 'Industrial/Agriculture Employment', + textAlign: SC.ALIGN_LEFT, + layout: {height:20, top: 0}, + backgroundColor: '#99CCFF' + + }), + manufacturingView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 21, height: 17}, + buildingUseProperty: 'percent', + category: 'Manufacturing' + }), + wholesaleView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 39, height: 17}, + buildingUseProperty: 'percent', + category: 'Wholesale' + }), + transportWarehousingServicesView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 57, height: 17}, + buildingUseProperty: 'percent', + category: 'Transport Warehousing' + }), + constructionUtilitiesView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 75, height: 17}, + buildingUseProperty: 'percent', + category: 'Construction Utilities' + }), + agricultureView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 93, height: 17}, + buildingUseProperty: 'percent', + category: 'Agriculture' + }), + extractionView:Footprint.EditableBuildingUseFieldView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + layout: {top: 111, height: 17}, + buildingUseProperty: 'percent', + category: 'Extraction' + }) + }) + }) + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/editable_building_use_type_view.js b/sproutcore/apps/fp/views/info_views/built_form/editable_building_use_type_view.js new file mode 100644 index 000000000..d68ea818b --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/editable_building_use_type_view.js @@ -0,0 +1,64 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 11/26/13 + * Time: 3:04 PM + * To change this template use File | Settings | File Templates. + */ + +sc_require('views/info_views/built_form/editable_input_field_view'); + +Footprint.editableBuildingUseTypeView = SC.View.extend({ + childViews:'titleView residentialView retailView officeView industrialView'.w(), + classNames: ['footprint-editable-building-use-types-view'], + content: null, + layout: null, + + titleView: SC.View.extend({ + childViews:'useRateTitle efficiencyTitle sqftPerUnitTitle'.w(), + useRateTitle: SC.LabelView.extend({ + value: 'Use Types', + fontWeight: 700, + textAlign: SC.ALIGN_CENTER, + layout: {left: 0, width: 0.35, height:24, top: 0} + }), + efficiencyTitle: SC.LabelView.extend({ + value: 'Use Efficiency', + fontWeight: 700, + textAlign: SC.ALIGN_CENTER, + layout: {left: 0.35, width: 0.25, height:24, top: 0} + }), + sqftPerUnitTitle: SC.LabelView.extend({ + value: 'SqFt Per Unit', + fontWeight: 700, + textAlign: SC.ALIGN_CENTER, + layout: {left: 0.6, width: 0.4, height:24, top: 0} + }) + }), + + residentialView: Footprint.editableUseTypeRowView.extend({ + use: 'Residential', + efficiencyValueBinding: SC.Binding.oneWay('.parentView.content').buildingUseFilter('efficiency', 'Residential'), + sqftValueBinding: SC.Binding.oneWay('.parentView.content').buildingUseFilter('square_feet_per_unit', 'Residential'), + layout: {height:24, top: 25} + }), + retailView: Footprint.editableUseTypeRowView.extend({ + use: 'Retail', + efficiencyValueBinding: SC.Binding.oneWay('.parentView.content').buildingUseFilter('efficiency', 'Retail'), + sqftValueBinding: SC.Binding.oneWay('.parentView.content').buildingUseFilter('square_feet_per_unit', 'Retail'), + layout: {height:24, top: 50} + }), + officeView: Footprint.editableUseTypeRowView.extend({ + use: 'Office', + efficiencyValueBinding: SC.Binding.oneWay('.parentView.content').buildingUseFilter('efficiency', 'Office'), + sqftValueBinding: SC.Binding.oneWay('.parentView.content').buildingUseFilter('square_feet_per_unit', 'Office'), + layout: {height:24, top: 75} + }), + + industrialView: Footprint.editableUseTypeRowView.extend({ + use: 'Industrial', + efficiencyValueBinding: SC.Binding.oneWay('.parentView.content').buildingUseFilter('efficiency', 'Industrial'), + sqftValueBinding: SC.Binding.oneWay('.parentView.content').buildingUseFilter('square_feet_per_unit', 'Industrial'), + layout: {height:24, top: 100} + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/editable_built_form_top_view.js b/sproutcore/apps/fp/views/info_views/built_form/editable_built_form_top_view.js new file mode 100644 index 000000000..5699f5a50 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/editable_built_form_top_view.js @@ -0,0 +1,30 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 11/26/13 + * Time: 4:58 PM + * To change this template use File | Settings | File Templates. + */ + + +Footprint.EditableBuiltFormTopView = SC.View.extend({ + classNames: ['footprint-built-form-top-view'], + childViews:'nameTitleView contentView'.w(), + recordType: null, + content: null, + titleValue: null, + + layout: {left: 330, height:70, top: 0, width: 650}, + + + nameTitleView: SC.LabelView.extend({ + valueBinding: SC.Binding.oneWay('.parentView.titleValue'), + fontWeight: 700, + layout: {left: 30, width: 100, height:24, top: 18} + }), + contentView: Footprint.EditableModelStringView.extend({ + valueBinding: SC.Binding.oneWay('.parentView*content.name'), + layout: {left: 150, width: 300, height:35, top: 15}, + backgroundColor: '#F8F8F8' + }), +}) diff --git a/sproutcore/apps/fp/views/info_views/built_form/editable_input_field_view.js b/sproutcore/apps/fp/views/info_views/built_form/editable_input_field_view.js new file mode 100644 index 000000000..7aef78489 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/editable_input_field_view.js @@ -0,0 +1,218 @@ + + + +Footprint.editableInputFieldView = SC.View.extend({ + classNames: ['footprint-editable-input-view'], + childViews:'NameTitleView ContentView'.w(), + value: null, + content: null, + layout: null, + + NameTitleView: SC.LabelView.extend({ + valueBinding: SC.Binding.oneWay('.parentView.value'), + layout: {left:.01, width:.59} + }), + ContentView: Footprint.EditableModelStringView.extend({ + textAlign: SC.ALIGN_CENTER, + valueBinding: SC.Binding.oneWay('.parentView.content'), + layout: {left:.6}, + hint: '--', + backgroundColor: '#F8F8F8' + }) +}) + + +Footprint.summaryFieldView = SC.View.extend({ + classNames: ['footprint-summary-field-view'], + childViews:'nameTitleView contentView'.w(), + value: null, + content: null, + layout: null, + rawData: null, + titleTextAlignment: null, + + nameTitleView: SC.LabelView.extend({ + textAlignBinding: SC.Binding.oneWay('.parentView.titleTextAlignment'), + valueBinding: SC.Binding.oneWay('.parentView.value'), + layout: {width:.65}, + backgroundColor: '#99CCFF' + }), + contentView: SC.LabelView.extend({ + textAlign: SC.ALIGN_CENTER, + rawData: null, + rawDataBinding: SC.Binding.oneWay('.parentView.content'), + value: function(){ + var value = this.get('rawData') + if (value) + return parseFloat(this.get('rawData')).toFixed(1); + return '--' + }.property('rawData').cacheable(), + layout: {left:.65}, + hintEnabled: YES, + hint: '0', + backgroundColor: '#F8F8F8' + }) +}) + + +Footprint.editableTableField= SC.View.extend({ + classNames: ['footprint-editable-table-row-view'], + childViews:'NameTitleView ContentView'.w(), + content: null, + layout: null, + title: null, + + NameTitleView: SC.LabelView.extend({ + valueBinding: SC.Binding.oneWay('.parentView.title'), + layout: {left: 0, width: 0.8, height: 17}, + backgroundColor: '#E0E0E0' + }), + ContentView: Footprint.EditableModelStringView.extend({ + textAlign: SC.ALIGN_CENTER, + valueBinding: SC.Binding.oneWay('.parentView.content'), + layout: {left: 0.8, height: 17}, + hint: '--', + backgroundColor: '#F8F8F8' + }) +}) + + +Footprint.editableUseTypeRowView = SC.View.extend({ + childViews:'useTypeNameView efficiencyView sqftUnitView'.w(), + layout: null, + use: null, + efficiencyValue: null, + sqftValue: null, + + useTypeNameView: SC.LabelView.extend({ + valueBinding: SC.Binding.oneWay('.parentView.use'), + layout: {left: 6, top: 3, width: 0.35}, + textAlign: SC.ALIGN_LEFT + }), + efficiencyView: Footprint.EditableModelStringView.extend({ + valueBinding: SC.Binding.oneWay('.parentView.efficiencyValue'), + layout: {left: 0.35, width: 0.25}, + hint: '--', + textAlign: SC.ALIGN_CENTER, + backgroundColor: '#F8F8F8' + }), + sqftUnitView: Footprint.EditableModelStringView.extend({ + valueBinding: SC.Binding.oneWay('.parentView.sqftValue'), + textAlign: SC.ALIGN_CENTER, + layout: {left: 0.6, width: 0.4}, + hint: '--', + backgroundColor: '#F8F8F8' + }) +}) + + + +Footprint.editableBuiltFormSourceListView = SC.View.extend(SC.Control, { + classNames: ['footprint-built-form-percent-list-scroll-view'], + layout: { height: 24 }, + childViews: 'nameLabelView dwellingUnitLabelView employmentLabelView percentLabelView'.w(), + duValue: null, + empValue: null, + nameValue: null, + percentValue: null, + + nameLabelView: SC.LabelView.extend({ + layout: { left: 0, width:.69 }, + valueBinding: SC.Binding.oneWay('.parentView.nameValue') + + }), + dwellingUnitLabelView: SC.LabelView.extend({ + layout: { left: 0.69, width:.08 }, + + rawData: null, + rawDataBinding: SC.Binding.oneWay('.parentView.duValue'), + value: function(){ + return parseFloat(this.get('rawData')).toFixed(1); + }.property('rawData').cacheable(), + backgroundColor: '#F0F8FF', + textAlign: SC.ALIGN_CENTER + }), + employmentLabelView: SC.LabelView.extend({ + layout: { left: 0.77, width:.08 }, + rawData: null, + rawDataBinding: SC.Binding.oneWay('.parentView.empValue'), + value: function(){ + return parseFloat(this.get('rawData')).toFixed(1); + }.property('rawData').cacheable(), + backgroundColor: '#F0F8FF', + textAlign: SC.ALIGN_CENTER + }), + + percentLabelView: SC.LabelView.extend({ + layout: { left: 0.85 }, + rawData: null, + rawDataBinding: SC.Binding.oneWay('.parentView.percentValue'), + value: function(){ + return parseFloat(this.get('rawData')).toFixed(2); + }.property('rawData').cacheable(), + backgroundColor: '#F0F8FF', + textAlign: SC.ALIGN_CENTER + }) +}) + + +Footprint.EditableBuildingUseFieldView = SC.View.extend({ + + classNames: ['footprint-editable-list-view'], + childViews:'nameTitleView contentView'.w(), + content: null, + layout: null, + buildingUseProperty: null, + category: null, + + + nameTitleView: SC.LabelView.extend({ + valueUpdateObserver: function () { + if (this.get('content')) { + this.setPath('parentView.parentView.layerNeedsUpdate', YES); + this.setPath('parentView.layerNeedsUpdate', YES); + this.set('layerNeedsUpdate', YES); + } + }.observes('.content'), + valueBinding: SC.Binding.oneWay('.parentView.category'), + layout: {left: 0, width: 0.8, height: 17} + }), + contentView: SC.LabelView.extend({ + textAlign: SC.ALIGN_CENTER, + buildingUseProperty: null, + buildingUsePropertyBinding: SC.Binding.oneWay('.parentView.buildingUseProperty'), + category: null, + categoryBinding: SC.Binding.oneWay('.parentView.category'), + content: null, + contentBinding:SC.Binding.oneWay('.parentView.content'), + value: function(){ + var buildingUseProperty = this.get('buildingUseProperty'); + var category = this.get('category'); + var content = this.get('content'); + if (!content) + return '--'; + var building_attribute_set = content.filter(function(building_use,i) { + return building_use.getPath('building_use_definition.name') == category})[0]; + if (!building_attribute_set) + return '--' + return building_attribute_set.get(buildingUseProperty); + }.property('content', 'category', 'buildingUseProperty', 'building_use_definition.name').cacheable(), + + layout: {left: 0.8, height: 17}, + backgroundColor: '#F8F8F8', + classNameBindings: ['isEditable'], // adds the is-editable when isEditable is YES + isEditable:YES, + needsEllipsis:YES, + renderDelegateName:'ellipsesLabelRenderDelegate', + mouseDown: function(evt) { + // Capture the event if it's a double click and we are editable. + return this.get('isEditable') && evt.clickCount === 2; + } + }) +}) + + + + + + diff --git a/sproutcore/apps/fp/views/info_views/built_form/editable_placetype_attributes_view.js b/sproutcore/apps/fp/views/info_views/built_form/editable_placetype_attributes_view.js new file mode 100644 index 000000000..a1ba04096 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/editable_placetype_attributes_view.js @@ -0,0 +1,48 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/5/13 + * Time: 12:32 PM + * To change this template use File | Settings | File Templates. + */ +sc_require('views/info_views/built_form/editable_input_field_view'); +sc_require('views/info_views/built_form/built_form_color_picker_view'); + +Footprint.EditablePlacetypeAttributesView = SC.View.extend({ + + classNames: ['footprint-editable-placetypes-attributes-view'], + childViews:'titleView placetypePercentScrollView BuiltFormColorPickerView'.w(), + layout: {bottom: 40, left: 340, right: 280, top:70}, + + titleView: SC.LabelView.extend({ + value: 'Included Building Types in Placetype', + layout: {left: 30, width: 250, height: 24, top: 0} + }), + + placetypePercentScrollView: SC.ScrollView.extend({ + classNames: ['footprint-placetype-percent-scroll-view'], + layout: {left: 40, width: 590, top: 25, height: 200}, + + contentView: SC.SourceListView.extend({ + isEnabledBinding: SC.Binding.oneWay('.content').bool(), + rowHeight: 20, + isEditable: YES, + actOnSelect: NO, + canEditContent: YES, + canDeleteContent: YES, + canReorderContent: YES, + contentBinding: SC.Binding.oneWay('Footprint.placetypesEditController*selection.firstObject.placetype_component_percents'), + selectionBinding: SC.Binding.from('Footprint.placetypesEditController.selection.firstObject'), + contentValueKey: 'name', + + exampleView: Footprint.editableBuiltFormSourceListView.extend({ + nameValueBinding:SC.Binding.oneWay('*content.placetype_component.name'), + duValueBinding:SC.Binding.oneWay('*content.placetype_component.building_attribute_set.flat_building_densities.dwelling_unit_density'), + empValueBinding:SC.Binding.oneWay('*content.placetype_component.building_attribute_set.flat_building_densities.employment_density'), + percentValueBinding: SC.Binding.oneWay('*content.percent') + }) + }) + }), + + BuiltFormColorPickerView: Footprint.BuiltFormColorPickerView +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/manage_building_type_view.js b/sproutcore/apps/fp/views/info_views/built_form/manage_building_type_view.js new file mode 100644 index 000000000..64cb26c07 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/manage_building_type_view.js @@ -0,0 +1,91 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/geography_info_view'); +sc_require('views/info_views/tags_info_view'); +sc_require('views/info_views/medium_info_view'); +sc_require('views/sections/built_form_section_view'); +sc_require('views/info_views/color_info_view'); +sc_require('models/built_form_models'); +sc_require('views/info_views/built_form/editable_built_form_top_view'); +sc_require('views/info_views/built_form/editable_building_type_attributes_view'); +sc_require('views/info_views/built_form/built_form_summary_attributes_view'); +sc_require('views/info_views/built_form/built_form_items_scroll_view'); + + +Footprint.ManageBuildingTypeView= SC.View.extend(SC.ActionSupport,{ + classNames: ['footprint-add-building-type-view'], + layout: {top:27}, + + childViews:['overlayView', 'builtFormTopView', 'editableBuildingTypeAttributesView', 'buildingTypeSummaryAttributesView', 'buildingTypeListView', 'buttonsView'], + + recordType: Footprint.PlacetypeComponent, + + content: null, + contentBinding: SC.Binding.oneWay('Footprint.buildingTypesEditController.content'), + + selection: null, + selectionBinding: SC.Binding.from('Footprint.buildingTypesEditController.selection'), + + selectedItem: null, + selectedItemBinding: SC.Binding.from('*selection.firstObject'), + + overlayView: Footprint.OverlayView.extend({ + layout: { left:10, width:330, bottom: 40, top: 20, zIndex:9999}, + contentBinding: SC.Binding.from('.parentView.content'), + statusBinding:SC.Binding.oneWay('*content.status') + }), + + builtFormTopView: Footprint.EditableBuiltFormTopView.extend({ + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + contentBinding: SC.Binding.oneWay('.parentView.selectedItem'), + titleValue: 'Building Type:' + }), + + editableBuildingTypeAttributesView: Footprint.EditableBuildingTypeAttributesView, + + buildingTypeSummaryAttributesView: Footprint.BuiltFormSummaryAttributesView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.selectedItem') + }), + + buildingTypeListView: Footprint.BuiltFormItemsScrollView.extend({ + layout: { left:10, width:330, bottom: 40, top: 0, zIndex: 1}, + contentBinding: SC.Binding.oneWay('.parentView.content'), + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + selectionBinding: SC.Binding.from('.parentView.selection') + }), + + buttonsView: SC.View.extend({ + layout: { bottom: 0, right: 0, height: 40 }, + childViews: 'closeButtonView saveButtonView saveAsButtonView'.w(), + classNames: ['footprint-add-built-form-buttons-view'], + closeButtonView: SC.ButtonView.design({ + layout: {bottom: 5, left: 20, height:24, width:80}, + title: 'Close', + action: 'doCancel', + isCancel: YES + }), + saveButtonView: SC.ButtonView.design({ + layout: {bottom: 5, left: 360, height:24, width:80}, + title: 'Save', + action: '' + }), + saveAsButtonView: SC.ButtonView.design({ + layout: {bottom: 5, left: 450, height:24, width:80}, + title: 'Save As', + action: '' + }) + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/manage_building_view.js b/sproutcore/apps/fp/views/info_views/built_form/manage_building_view.js new file mode 100644 index 000000000..e4f1129c2 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/manage_building_view.js @@ -0,0 +1,100 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/geography_info_view'); +sc_require('views/info_views/tags_info_view'); +sc_require('views/info_views/medium_info_view'); +sc_require('views/sections/built_form_section_view'); +sc_require('views/info_views/color_info_view'); +sc_require('models/built_form_models'); +sc_require('views/info_views/built_form/editable_building_attributes_view'); +sc_require('views/info_views/built_form/editable_building_use_percents_view'); +sc_require('views/info_views/built_form/editable_built_form_top_view'); +sc_require('views/info_views/built_form/built_form_summary_attributes_view'); +sc_require('views/info_views/built_form/built_form_items_scroll_view'); +sc_require('views/info_views/built_form/editable_building_types_tagged_buildings_view'); + + +/*** + * The pane used to edit the settings of a new or existing BuiltForm and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the BuiltForm if a DbEntity is being created here + * @type {*} + */ +Footprint.ManageBuildingView = SC.View.extend({ + classNames: ['footprint-add-building-view'], + layout: {top:27}, + + childViews:['overlayView', 'builtFormTopView', 'editableBuildingUsePercentView', 'editableBuildingAttributesView', 'buildingSummaryAttributesView', 'buildingTagView', 'buildingListView', 'buttonsView'], + + recordType: Footprint.PrimaryComponent, + + content: null, + contentBinding: SC.Binding.oneWay('Footprint.buildingsEditController.content'), + + selection: null, + selectionBinding: SC.Binding.from('Footprint.buildingsEditController.selection'), + + selectedItem: null, + selectedItemBinding: SC.Binding.from('*selection.firstObject'), + + overlayView: Footprint.OverlayView.extend({ + layout: { left:10, width:330, bottom: 40, top: 20, zIndex:9999}, + contentBinding: SC.Binding.from('.parentView.content'), + statusBinding:SC.Binding.oneWay('*content.status') + }), + + builtFormTopView: Footprint.EditableBuiltFormTopView.extend({ + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + contentBinding: SC.Binding.oneWay('.parentView.selectedItem'), + titleValue: 'Building Name:' + }), + editableBuildingUsePercentView: Footprint.EditableBuildingUsePercentView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.selectedItem') + }), + editableBuildingAttributesView: Footprint.EditableBuildingAttributesView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.selectedItem') + }), + buildingSummaryAttributesView: Footprint.BuiltFormSummaryAttributesView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.selectedItem') + }), + + buildingTagView: Footprint.EditableBuildingTypesTaggedBuildingsView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.selectedItem'), + selectionBinding: SC.Binding.from('.parentView.selection') + }), + + buildingListView: Footprint.BuiltFormItemsScrollView.extend({ + layout: { left:10, width:330, bottom: 40, top: 0, zIndex: 1}, + contentBinding: SC.Binding.oneWay('.parentView.content'), + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + selectionBinding: SC.Binding.from('.parentView.selection') + }), + + buttonsView: SC.View.extend({ + layout: { bottom: 0, right: 0, height: 40 }, + childViews: 'closeButtonView saveButtonView'.w(), + classNames: ['footprint-add-building-buttons-view'], + closeButtonView: SC.ButtonView.design({ + layout: {bottom: 5, left: 20, height:24, width:80}, + title: 'Close', + action: 'doCancel', + isCancel: YES + }), + saveButtonView: SC.ButtonView.design({ + layout: {bottom: 5, left: 360, height:24, width:80}, + title: 'Save', + action: 'doSave' + }) + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/built_form/manage_placetype_view.js b/sproutcore/apps/fp/views/info_views/built_form/manage_placetype_view.js new file mode 100644 index 000000000..ca8bf181a --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/built_form/manage_placetype_view.js @@ -0,0 +1,96 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/5/13 + * Time: 9:53 AM + * To change this template use File | Settings | File Templates. + */ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/geography_info_view'); +sc_require('views/info_views/tags_info_view'); +sc_require('views/info_views/medium_info_view'); +sc_require('views/sections/built_form_section_view'); +sc_require('views/info_views/color_info_view'); +sc_require('models/built_form_models'); +sc_require('views/info_views/built_form/editable_built_form_top_view'); +sc_require('views/info_views/built_form/editable_placetype_attributes_view'); +sc_require('views/info_views/built_form/built_form_summary_attributes_view'); +sc_require('views/info_views/built_form/built_form_items_scroll_view'); + + +Footprint.ManagePlacetypeView = SC.View.extend(SC.ActionSupport,{ + + childViews:['overlayView', 'builtFormTopView', 'editablePlacetypeAttributesView', 'placetypeSummaryAttributesView', 'placetypeListView', 'buttonsView'], + layout: {top:27}, + + recordType: Footprint.Placetype, + + content: null, + contentBinding: SC.Binding.oneWay('Footprint.placetypesEditController.content'), + + selection: null, + selectionBinding: SC.Binding.from('Footprint.placetypesEditController.selection'), + + selectedItem: null, + selectedItemBinding: SC.Binding.from('*selection.firstObject'), + + overlayView: Footprint.OverlayView.extend({ + layout: { left:10, width:330, bottom: 40, top: 20, zIndex: 9999}, + contentBinding: SC.Binding.from('.parentView.content'), + statusBinding:SC.Binding.oneWay('*content.status') + }), + + builtFormTopView: Footprint.EditableBuiltFormTopView.extend({ + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + contentBinding: SC.Binding.oneWay('.parentView.selectedItem'), + titleValue: 'Placetype:' + }), + editablePlacetypeAttributesView: Footprint.EditablePlacetypeAttributesView.extend({ + }), + placetypeSummaryAttributesView: Footprint.BuiltFormSummaryAttributesView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.selectedItem') + }), + + placetypeListView: Footprint.BuiltFormItemsScrollView.extend({ + layout: { left:10, width:330, bottom: 40, top: 0, zIndex: 1}, + contentBinding: SC.Binding.oneWay('.parentView.content'), + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + selectionBinding: SC.Binding.from('.parentView.selection') + }), + + buttonsView: SC.View.extend({ + layout: { bottom: 0, right: 0, height: 40 }, + childViews: 'closeButtonView saveButtonView saveAsButtonView'.w(), + classNames: ['footprint-add-built-form-buttons-view'], + closeButtonView: SC.ButtonView.design({ + layout: {bottom: 5, left: 20, height:24, width:80}, + title: 'Close', + action: 'doCancel', + isCancel: YES + }), + saveButtonView: SC.ButtonView.design({ + layout: {bottom: 5, left: 360, height:24, width:80}, + title: 'Save', + action: '' + }), + saveAsButtonView: SC.ButtonView.design({ + layout: {bottom: 5, left: 450, height:24, width:80}, + title: 'Save As', + action: '' + }) + }) +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/categories_info_view.js b/sproutcore/apps/fp/views/info_views/categories_info_view.js new file mode 100644 index 000000000..88d230c6f --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/categories_info_view.js @@ -0,0 +1,24 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + + +Footprint.CategoriesInfoView = Footprint.SelectUpdateInfoView.extend({ + classNames: "footprint-categories-info-view".w(), + title: 'Categories', + + contentView: Footprint.InfoView.extend({ + // TODO redo + contentView:SC.View + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/color_info_view.js b/sproutcore/apps/fp/views/info_views/color_info_view.js new file mode 100644 index 000000000..be525eb70 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/color_info_view.js @@ -0,0 +1,19 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.ColorInfoView = Footprint.InfoView.extend({ + classNames: "footprint-color-info-view".w(), + contentView:SC.View +}); diff --git a/sproutcore/apps/fp/views/info_views/config_entity/editable_clone_field_view.js b/sproutcore/apps/fp/views/info_views/config_entity/editable_clone_field_view.js new file mode 100644 index 000000000..7a0ab8fc0 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/config_entity/editable_clone_field_view.js @@ -0,0 +1,28 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 12/11/13 + * Time: 10:25 AM + * To change this template use File | Settings | File Templates. + */ + + +Footprint.EditableCloneFieldView = SC.View.extend({ + classNames:'footprint-editable-clone-field-view'.w(), + childViews:['titleView', 'editableContentView'], + + value: null, + title:null, + layout: null, + + titleView: SC.LabelView.extend({ + layout: { height: 17 }, + valueBinding: SC.Binding.oneWay('.parentView.title') + }), + + editableContentView: Footprint.EditableModelStringView.extend({ + classNames:'footprint-editable-clone-field-content-view'.w(), + layout: { top: 17, left: 15 }, + valueBinding: '.parentView.value' + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/config_entity/project_info_pane.js b/sproutcore/apps/fp/views/info_views/config_entity/project_info_pane.js new file mode 100644 index 000000000..e5770482a --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/config_entity/project_info_pane.js @@ -0,0 +1,70 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Project Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/geography_info_view'); +sc_require('views/info_views/tags_info_view'); +sc_require('views/info_views/medium_info_view'); +sc_require('views/info_views/categories_info_view'); + +/*** + * The pane used to edit the settings of a new or existing PresentationMedium and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the PresentationMedium if a DbEntity is being created here + * @type {*} + */ +Footprint.ProjectInfoPane = Footprint.InfoPane.extend({ + layout: { top: 0, left: 0, width: 400, height: 400 }, + classNames: 'footprint-project-info-view'.w(), + + recordType: Footprint.Project, + + controllers: Footprint.projectControllers, + + contentView: Footprint.ContentView.extend({ + childViews: ['nameView', 'yearView', 'keyView', 'descriptionView', 'geographyView', 'categoriesView'], + + refreshTarget: Footprint.ProjectToolbarView, + controllers: Footprint.projectControllers, + // Bind to the controller so children access the content items by delegation + contentBinding: parentViewPath(1, '*editController'), + + nameView: Footprint.NameInfoView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .05} + }), + yearView: Footprint.YearItemView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .05} + }), + keyView: Footprint.KeyInfoView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .1} + }), + descriptionView: Footprint.DescriptionItemView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .1} + }), + geographyView: Footprint.GeographyInfoView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .1} + }), + categoriesView: Footprint.CategoriesInfoView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .1} + }), + commitButtonsView: Footprint.InfoPaneButtonsView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .1} + }) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/config_entity/scenario_info_pane.js b/sproutcore/apps/fp/views/info_views/config_entity/scenario_info_pane.js new file mode 100644 index 000000000..87f5488da --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/config_entity/scenario_info_pane.js @@ -0,0 +1,108 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/config_entity/editable_clone_field_view'); +sc_require('views/info_views/edit_records_select_view'); +sc_require('views/cancel_button_view'); + +/*** + * The pane used to edit the settings of a new or existing PresentationMedium and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the PresentationMedium if a DbEntity is being created here + * @type {*} */ +Footprint.ScenarioInfoPane = SC.PanelPane.extend({ + + layout: { width: 600, height: 300, centerX: 0, centerY: 0 }, + classNames:'footprint-scenario-info-view'.w(), + + recordType:Footprint.Scenario, + + content: null, + contentBinding: SC.Binding.oneWay('Footprint.scenariosEditController.content'), + selection: null, + selectionBinding: SC.Binding.from('Footprint.scenariosEditController.selection'), + + contentView: SC.View.extend({ + classNames:'footprint-info-content-view'.w(), + childViews:['titleView', 'scenarioSelectView', 'editableContentView', 'overlayView', 'scenarioButtonViews'], + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + selection: null, + selectionBinding: SC.Binding.from('.parentView.selection'), + + titleView: SC.LabelView.extend({ + layout: { left: 10, height: 25, top: 5 }, + classNames: ['footprint-info-title-view'], + value: 'Manage Scenarios' + }), + + scenarioSelectView: Footprint.EditRecordsSelectView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + selectionBinding: SC.Binding.from('.parentView.selection') + }), + + overlayView: Footprint.OverlayView.extend({ + layout: { top: 30, left: 245, bottom: 40, right: 20 }, + testItems: YES, + contentBinding: SC.Binding.from('.parentView.content'), + statusBinding:SC.Binding.oneWay('*content.status') + }), + editableContentView: SC.View.extend({ + layout: { top: 30, left: 245, bottom: 40, right: 20 }, + classNames:'footprint-scenario-info-editable-content-view'.w(), + childViews:['nameView', 'yearView', 'descriptionView'], + + content: null, + contentBinding: SC.Binding.oneWay('.parentView.selection'), + + nameView: Footprint.EditableCloneFieldView.extend({ + layout: { height: 40 }, + valueBinding: '.parentView*content.firstObject.name', + title: 'Scenario Name' + }), + + yearView: Footprint.EditableCloneFieldView.extend({ + layout: { top: 45, height: 40 }, + valueBinding: '.parentView*content.firstObject.year', + title: 'Scenario Year' + }), + + descriptionView: Footprint.EditableCloneFieldView.extend({ + layout: { top: 90, bottom: 0 }, + valueBinding: '.parentView*content.firstObject.description', + title: 'Description' + }) + }), + + scenarioButtonViews: SC.View.extend({ + layout: { bottom: 0, height: 30, left: 245, right: 20 }, + childViews:['cancelButtonView', 'saveButtonView'], + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + // Disable buttons unless all items are READY + isEnabledBinding: SC.Binding.oneWay('.content').allMatchStatus(SC.Record.READY), + + cancelButtonView: Footprint.CancelButtonView.extend({ + layout: { bottom: 5, right: 130, height: 24, width: 80 }, + calculatedStatusBinding: SC.Binding.oneWay('Footprint.scenariosEditController.calculatedStatus'), + }), + saveButtonView: SC.ButtonView.design({ + layout: { bottom: 5, right: 0, height: 24, width: 120 }, + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + title: 'Save All Changes', + action: 'doSave' + }) + }) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/edit_records_select_view.js b/sproutcore/apps/fp/views/info_views/edit_records_select_view.js new file mode 100644 index 000000000..8a580e255 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/edit_records_select_view.js @@ -0,0 +1,79 @@ +Footprint.EditRecordsSelectView = SC.ScrollView.extend({ + layout: {width:.36, left: 10, bottom: 10, top: 30}, + content: null, + selection: null, + /*** + * A property path relative to each item's content used for the name. Defaults to 'name' + */ + contentNameProperty: 'name', + + contentView: SC.SourceListView.extend({ + isEnabledBinding: SC.Binding.oneWay('.content').bool(), + rowHeight: 24, + actOnSelect: NO, + canReorderContent: NO, + + contentBinding: SC.Binding.oneWay('.parentView.parentView.content'), + selectionBinding: SC.Binding.from('.parentView.parentView.selection'), + + // Show the selection so the user has any idea where they are. + selectionDidChange: function() { + this.invokeNext(this._selectionDidChange); + }.observes('selection'), + + _selectionDidChange: function() { + var sel = this.get('selection'), + content = this.get('content'), + selIndexes, scrollTo; + if (sel && content) { + selIndexes = sel.indexSetForSource(content); + scrollTo = selIndexes ? selIndexes.get('min') : 0; + this.scrollToContentIndex(scrollTo); + } + }, + + exampleView: SC.View.extend(SC.Control, { + layout: { height: 24 }, + childViews: 'nameLabelView copyButtonView deleteButtonView progressOverlayView'.w(), + classNameBindings: ['isNew:new-record', 'isDirty:dirty-record'], + + status: null, + statusBinding: SC.Binding.oneWay('*content.status'), + + isNew: function() { + return this.get('status') === SC.Record.READY_NEW; + }.property('status').cacheable(), + + isDirty: function() { + return this.get('status') === SC.Record.READY_DIRTY; + }.property('status').cacheable(), + + nameLabelView: SC.LabelView.extend({ + layout: { left: 48, top: 0.3 }, + contentBinding: SC.Binding.oneWay('.parentView.content'), + contentValueKey:'name' + }), + + copyButtonView: Footprint.CopyButtonView.extend({ + layout: { left: 0, width: 16, centerY: 0, height: 16 }, + action: 'doCloneRecord', + contentBinding: SC.Binding.oneWay('.parentView.content'), + isVisible: function() { + // Only allow clone on existing records + return (this.getPath('content.id') || 0) > 0 + }.property('content').cacheable() + }), + + deleteButtonView: Footprint.DeleteButtonView.extend({ + layout: { left: 24, width: 16, centerY: 0, height: 16}, + action: 'doPromptDeleteRecord', + contentBinding: SC.Binding.oneWay('.parentView.content') + }), + + progressOverlayView: Footprint.ProgressOverlayView.extend({ + layout: { left:.5, width:.5, centerY: 0, height: 16}, + contentBinding: SC.Binding.oneWay('.parentView.content') + }) + }) + }) +}) diff --git a/sproutcore/apps/fp/views/info_views/features/feature_edit_info_view.js b/sproutcore/apps/fp/views/info_views/features/feature_edit_info_view.js new file mode 100644 index 000000000..654086ab8 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/features/feature_edit_info_view.js @@ -0,0 +1,88 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/info_views/query_info_view'); + +/*** + * The pane used to edit the settings of a new or existing Feature and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the Feature if a DbEntity is being created here + * @type {*} + */ +Footprint.FeatureEditInfoView = SC.View.extend({ + classNames: "footprint-feature-edit-info-view".w(), + childViews: 'contentView'.w(), + recordType: null, + selection: null, + content: null, + + + // TODO ideally these are in the declaration of the view subclass + contentBinding: SC.Binding.oneWay('*parentView.parentView.content'), + recordTypeBinding: SC.Binding.oneWay('*parentView.parentView.recordType'), + selectionBinding: '*parentView.parentView.selection', + + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('.parentView.parentView.layerSelection'), + + contentView: Footprint.ContentView.extend({ + childViews: 'queryView summaryView editView commitButtonsView'.w(), + contentBinding: SC.Binding.oneWay('.parentView.content'), + selection: null, + selectionBinding: '.parentView.selection', + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('.parentView.layerSelection'), + + queryView: Footprint.QueryInfoView.extend({ + layout: {top:10, height: 102}, + contentBinding: SC.Binding.oneWay('.parentView.layerSelection'), + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + showSummaryFields: NO + }), + + summaryView: Footprint.TableInfoView.extend({ + layout: {top: 114, bottom: 0, width: 0.65}, + contentBinding: SC.Binding.oneWay('.parentView.content'), + selectionBinding: '.parentView.selection', + countBinding: SC.Binding.oneWay('.parentView.content').lengthOf(), + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + layerSelectionBinding: SC.Binding.oneWay('.parentView.layerSelection') + }), + + editView: SC.ContainerView.extend({ + layout: {top: 114, bottom: 0, left: 0.65, right:0}, + + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + contentBinding: SC.Binding.oneWay('.parentView.content'), + + nowShowingDefault:'defaultEditView', + nowShowing: function() { + if (this.get('recordType')) { + // Look for a view matching the recordType that adds the suffix InfoView to the record type name + var view = SC.objectForPropertyPath('%@InfoView'.fmt(this.get('recordType'))); + if (view) { + return view.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content') + }) + } + } + return this.get('nowShowingDefault'); + }.property('recordType').cacheable(), + defaultEditView:SC.View + }), + + commitButtonsView: Footprint.InfoPaneButtonsView + }) + +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/features/feature_info_pane.js b/sproutcore/apps/fp/views/info_views/features/feature_info_pane.js new file mode 100644 index 000000000..0f822d298 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/features/feature_info_pane.js @@ -0,0 +1,80 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/info_views/query_info_view'); +sc_require('views/info_views/features/feature_summary_info_view'); +sc_require('views/info_views/features/feature_query_info_view'); +sc_require('views/info_views/features/feature_edit_info_view'); + +/*** + * The pane used to edit the settings of a new or existing Feature and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the Feature if a DbEntity is being created here + * @type {*} + */ + + +Footprint.FeatureInfoPane = Footprint.InfoPane.extend({ + classNames: "footprint-feature-info-view opaque".w(), + // context passed in upon creation. This may contain 'nowShowing' + context: null, + + recordType: null, + recordTypeBinding: SC.Binding.oneWay('Footprint.layerActiveController.featureRecordType'), + content:null, + contentBinding: SC.Binding.oneWay('Footprint.featuresActiveController.content'), + selection: null, // TODO always null for now + summaryContent:null, + summaryContentBinding: SC.Binding.oneWay('Footprint.featureSummariesActiveController.content'), + summarySelection: null, // TODO always null for now + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('Footprint.layerSelectionEditController.content'), + + layout: { top: 0, width: 950, right: 0, height: 550 }, + + /*** + * This checks context for nowShowing. You can override this property with a hard-coded nowShowing + */ + nowShowing:function() { + return this.getPath('context.nowShowing') || 'Footprint.FeatureSummaryInfoView'; + }.property('context').cacheable(), + + contentView: SC.TabView.design({ + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + selection: null, + selectionBinding: '.parentView.selection', + recordType: null, + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + + summaryContent: null, + summaryContentBinding: SC.Binding.oneWay('.parentView.summaryContent'), + summarySelection: null, + summarySelectionBinding: '.parentView.summarySelection', + + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('.parentView.layerSelection'), + + layout: {top: 10}, + itemTitleKey: 'title', + itemValueKey: 'value', + items: [ + {title: 'Summary', value: 'Footprint.FeatureSummaryInfoView'}, + {title: 'Query', value: 'Footprint.FeatureQueryInfoView'}, + {title: 'Edit', value: 'Footprint.FeatureEditInfoView'} + ], + // Two way binding so that the view can receive the initial value from the parent but also change it + nowShowingBinding: '.parentView.nowShowing' + }) + + +}); diff --git a/sproutcore/apps/fp/views/info_views/features/feature_query_info_view.js b/sproutcore/apps/fp/views/info_views/features/feature_query_info_view.js new file mode 100644 index 000000000..96fe95341 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/features/feature_query_info_view.js @@ -0,0 +1,168 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/info_views/query_info_view'); + +/*** + * The pane used to edit the settings of a new or existing Feature and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the Feature if a DbEntity is being created here + * @type {*} + */ +Footprint.FeatureQueryInfoView = SC.View.extend({ + classNames: "footprint-feature-query-info-view".w(), + childViews: 'contentView'.w(), + recordType: null, + selection: null, + content: null, + summarySelection: null, + summaryContent: null, + + // TODO ideally these are in the declaration of the view subclass + contentBinding: SC.Binding.oneWay('.parentView.parentView.content'), + recordTypeBinding: SC.Binding.oneWay('.parentView.parentView.recordType'), + selectionBinding: '.parentView.parentView.selection', + + summaryContentBinding: SC.Binding.oneWay('.parentView.parentView.summaryContent'), + summarySelectionBinding: '.parentView.parentView.summarySelection', + + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('.parentView.parentView.layerSelection'), + + contentView: Footprint.ContentView.extend({ + childViews: 'queryView resultsView summaryResultsView commitButtonsView bufferView'.w(), + contentBinding: SC.Binding.oneWay('.parentView.content'), + selection: null, + selectionBinding: '.parentView.selection', + + summaryContent: null, + summarySelection: null, + summaryContentBinding: SC.Binding.oneWay('.parentView.summaryContent'), + summarySelectionBinding: '.parentView.summarySelection', + + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('.parentView.layerSelection'), + + queryView: Footprint.QueryInfoView.extend({ + layout: {top:10, height: 110}, + contentBinding: SC.Binding.oneWay('.parentView.layerSelection'), + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType') + }), + + resultsView: Footprint.TableInfoView.extend({ + classNames: "footprint-query-info-results-view".w(), + layout: {top: 125, bottom:.05, left:0, right:0.4}, + title: function() { + return '%@ Query or Selection Results'.fmt( + this.getPath('content.length')!=null ? this.getPath('content.length') : 'Loading'); + }.property('content').cacheable(), + contentBinding: SC.Binding.oneWay('.parentView.content'), + contentStatus:null, + contentStatusBinding: SC.Binding.oneWay('*content.status'), + + selectionBinding: '.parentView.selection', + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('.parentView.layerSelection'), + layerSelectionStatus: null, + layerSelectionStatusBinding: SC.Binding.oneWay('*layerSelection.status'), + + // The overlay is visible if either feature of layerSelection status is BUSY + overlayStatus: function() { + return Math.max(this.get('contentStatus'), this.get('layerSelectionStatus')); + }.property('contentStatus', 'layerSelectionStatus').cacheable(), + + columns: function() { + if (!(this.get('layerSelectionStatus') & SC.Record.READY)) + return []; + var layerSelection = this.get('layerSelection'); + return (layerSelection.get('result_fields') || []).map(function(field) { + return layerSelection.getPath('result_field_title_lookup.%@'.fmt(field)); + }); + }.property('layerSelectionStatus').cacheable(), + mapProperties: function() { + if (!(this.get('layerSelectionStatus') & SC.Record.READY)) + return SC.Object.create(); + return mapToSCObject( + Footprint.layerSelectionEditController.getPath('result_fields') || [], + function(field) { + return [Footprint.layerSelectionEditController.getPath('result_field_title_lookup.%@'.fmt(field)), field]; + }, + this + ); + }.property('layerSelectionStatus').cacheable(), + // Enable the zoom to selection button + zoomToSelectionIsVisible: YES + }), + + summaryResultsView: Footprint.TableInfoView.extend({ + classNames: "footprint-query-info-summary-results-view".w(), + layout: {top: 125, bottom:.05, left:0.62, right: 0}, + title: 'Feature Summary', + contentBinding: SC.Binding.oneWay('.parentView.summaryContent'), + + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('.parentView.layerSelection'), + layerSelectionStatus: null, + layerSelectionStatusBinding: SC.Binding.oneWay('*layerSelection.status'), + + // The overlay is visible if layerSelection status is BUSY + overlayStatus: function() { + return this.get('layerSelectionStatus'); + }.property('layerSelectionStatus').cacheable(), + + columns: function() { + if (!(this.get('layerSelectionStatus') & SC.Record.READY)) + return []; + var layerSelection = this.get('layerSelection'); + return (layerSelection.get('summary_fields') || []).map(function(field) { + return layerSelection.getPath('summary_field_title_lookup.%@'.fmt(field)); + }); + }.property('layerSelectionStatus').cacheable(), + mapProperties: function() { + if (!(this.get('layerSelectionStatus') & SC.Record.READY)) + return SC.Object.create(); + return mapToSCObject( + Footprint.layerSelectionEditController.getPath('summary_fields') || [], + function(field) { + return [Footprint.layerSelectionEditController.getPath('summary_field_title_lookup.%@'.fmt(field)), field]; + }, + this + ); + }.property('layerSelectionStatus').cacheable(), + selectionBinding: '.parentView.summarySelection', + countBinding: SC.Binding.oneWay('.parentView.summaryContent').lengthOf() + }), + + commitButtonsView: Footprint.InfoPaneButtonsView.extend({ + layout: {left: .04, top: .92, width: 0.2} + }), + + bufferView: SC.SegmentedView.extend({ + layout: { top:.94, height: 26, right: 0.01, width: 100 }, + selectSegmentWhenTriggeringAction: NO, + itemActionKey: 'action', + itemTitleKey: 'title', + itemKeyEquivalentKey: 'keyEquivalent', + itemValueKey: 'title', + itemIsEnabledKey: 'isEnabled', + + items: [ + // View and edit the selected item's attributes + SC.Object.create({ title: 'Undo', keyEquivalent: 'ctrl_u', action: 'doLayerSelectionUndo', isEnabledBinding: SC.Binding.oneWay('Footprint.layerSelectionActiveController*undoManager.canUndo').bool(), type: 'chronicler'}), + SC.Object.create({ title: 'Redo', keyEquivalent: 'ctrl_r', action: 'doLayerSelectionRedo', isEnabledBinding: SC.Binding.oneWay('Footprint.layerSelectionActiveController*undoManager.canRedo').bool(), type: 'chronicler'}) + ] + }) + }) + +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/features/feature_summary_info_view.js b/sproutcore/apps/fp/views/info_views/features/feature_summary_info_view.js new file mode 100644 index 000000000..0252e5ecd --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/features/feature_summary_info_view.js @@ -0,0 +1,56 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/info_views/query_info_view'); + +/*** + * The pane used to edit the settings of a new or existing Feature and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the Feature if a DbEntity is being created here + * @type {*} + */ +Footprint.FeatureSummaryInfoView = SC.View.extend({ + childViews: 'contentView'.w(), + classNames: "footprint-summary-info-view".w(), + recordType: null, + selection: null, + content: null, + + // TODO ideally these are in the declaration of the view subclass + contentBinding: SC.Binding.oneWay('.parentView.parentView.content'), + recordTypeBinding: SC.Binding.oneWay('.parentView.parentView.recordType'), + selectionBinding: '.parentView.parentView.selection', + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('.parentView.parentView.content'), + + contentView: Footprint.ContentView.extend({ + childViews: 'summaryView commitButtonsView'.w(), + contentBinding: SC.Binding.oneWay('.parentView.content'), + selection: null, + selectionBinding: '.parentView.selection', + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('.parentView.layerSelection'), + + summaryView: Footprint.TableInfoView.extend({ + layout: {top: 10, bottom: 0}, + contentBinding: SC.Binding.oneWay('.parentView.content'), + selectionBinding: '.parentView.selection', + countBinding: SC.Binding.oneWay('.parentView.content').lengthOf(), + recordTypeBinding: SC.Binding.oneWay('.parentView.recordType'), + layerSelectionBinding: SC.Binding.oneWay('.parentView.layerSelection') + }), + + commitButtonsView: Footprint.InfoPaneButtonsView + }) + +}); diff --git a/sproutcore/apps/fp/views/info_views/geography_info_view.js b/sproutcore/apps/fp/views/info_views/geography_info_view.js new file mode 100644 index 000000000..d89772a96 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/geography_info_view.js @@ -0,0 +1,24 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + + + +Footprint.GeographyInfoView = Footprint.InfoView.extend({ + classNames: "footprint-geography-info-view".w(), + + title:'Geography', + contentView: Footprint.EditableModelStringView.extend({ + valueBinding: parentViewPath(2, '*content.bounds') + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/key_info_view.js b/sproutcore/apps/fp/views/info_views/key_info_view.js new file mode 100644 index 000000000..23e146eb1 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/key_info_view.js @@ -0,0 +1,50 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + + + +// Allows the user to view and change the PresentationMedium's DbEntityInterest's DbEntity's key +// The user would most likely only change the key after uploading a new layer. For instance, if the +// user uploaded a new constraints layer they would want to key it with 'constraint' if they failed to so +// at the time of upload. +// In the future edits should only be permitted if the DbEntityInterest.interest indicates that the ConfigEntity +// owns the DbEntity. +Footprint.KeyInfoView = Footprint.InfoView.extend({ + classNames: "footprint-key-info-view".w(), + title: 'Key', + + contentView: SC.View.extend({ + layout: {left: .2, width: .8}, + /* + showKeySelect:YES, + isEditable:YES, + // The content of the key at keyitemPath + contentBinding: parentViewPath(2,'*content'), + + // The path from content to the selected key + keyItemPathBinding: parentViewPath(1,'*keyItemPath'), + + valueBinding: parentViewPath(3, '*editController.key'), + // An Object that organizes the DbEntities by key. Used to determine if the DbEntity has a unique key + itemsByKeyBinding: SC.Binding.oneWay(parentViewPath(3, '*recordSetController.dbEntityInterestsByKey')), + // In the event that a key is shared, this Object maps each key to the selected DbEntity, which + // determines if the item is checked + selectedItemByKeyBinding: SC.Binding.oneWay(parentViewPath(3, '*recordSetController.selectedDbEntityInterestsByKey')), + // All of the available keys from which to choose a new value. The user should also be able to make up + // a new key. + keysBinding: SC.Binding.oneWay(parentViewPath(3, '*recordSetController.keys')) + */ + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/layer/layer_info_pane.js b/sproutcore/apps/fp/views/info_views/layer/layer_info_pane.js new file mode 100644 index 000000000..054513cc4 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/layer/layer_info_pane.js @@ -0,0 +1,203 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/config_entity/editable_clone_field_view'); +sc_require('views/info_views/edit_records_select_view'); +sc_require('views/cancel_button_view'); + +/*** + * The pane used to edit the settings of a new or existing PresentationMedium and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the PresentationMedium if a DbEntity is being created here + * @type {*} */ +Footprint.LayerInfoPane = SC.PanelPane.extend({ + + layout: { width: 600, height: 300, centerX: 0, centerY: 0 }, + classNames:'footprint-layer-info-view'.w(), + + // Convenient UI flag. + layerFileIsUploading: NO, + + recordType: Footprint.Layer, + + content: null, + contentBinding: SC.Binding.oneWay('Footprint.layersEditController.content'), + selection: null, + selectionBinding: SC.Binding.from('Footprint.layersEditController.selection'), + + contentView: SC.View.extend({ + classNames:'footprint-info-content-view'.w(), + childViews:['titleView', 'layerSelectView', 'editableContentView', 'buttonViews', 'uploadingOverlay'], + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + selection: null, + selectionBinding: SC.Binding.from('.parentView.selection'), + + titleView: SC.LabelView.extend({ + layout: { left: 10, height: 25, top: 5 }, + classNames: ['footprint-info-title-view'], + value: 'Manage Layers' + }), + + layerSelectView: Footprint.EditRecordsSelectView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + selectionBinding: SC.Binding.from('.parentView.selection') + }), + + contentIsNew: NO, + contentIsNewBinding: SC.Binding.oneWay('Footprint.layerEditController.status').equalsStatus(SC.Record.READY_NEW), + + editableContentView: SC.View.extend({ + layout: { top: 30, left: 245, bottom: 40, right: 20 }, + classNames: 'footprint-layer-info-editable-content-view'.w(), + + childViews: ['nameView', 'sridView', 'hasFileView', 'uploadView', 'createFromLayerSelectionView'], + nameView: Footprint.EditableCloneFieldView.extend({ + valueBinding: 'Footprint.layerEditController*name', + title: 'Layer Name', + layout: { height: 40 } + }), + sridView: Footprint.EditableCloneFieldView.extend({ + layout: { top: 45, height: 40 }, + notOriginInstance: null, + notOriginInstanceBinding:SC.Binding.oneWay('Footprint.layerEditController.origin_instance').not(), + isVisibleBinding: SC.Binding.and('.parentView.parentView.contentIsNew', '.notOriginInstance'), + valueBinding: 'Footprint.layerEditController*db_entity_interest.db_entity.srid', + title: 'SRID' + }), + hasFileView: SC.LabelView.extend({ + layout: { top: 95, height: 20, width: 260, centerX: 0 }, + hasFileBinding: SC.Binding.oneWay('Footprint.layerEditController*db_entity_interest.db_entity.upload_id').bool(), + isNewBinding: SC.Binding.oneWay('.parentView.parentView.contentIsNew'), + isVisibleBinding: SC.Binding.and('.hasFile', '.isNew'), + value: 'File successfully uploaded, okay to save layer...' + }), + uploadView: SC.View.extend({ + layout: { top: 90, bottom: 40}, + notOriginInstance: null, + notOriginInstanceBinding:SC.Binding.oneWay('Footprint.layerEditController.origin_instance').not(), + isVisibleBinding: SC.Binding.and('.parentView.parentView.contentIsNew', '.notOriginInstance'), + childViews: ['uploadButtonView'], + + uploadButtonView: SC.FileChooserView.extend({ + layout: { height: 24, width: 140, centerX: 0, centerY: -10 }, + + content: null, + contentBinding: SC.Binding.oneWay('Footprint.layerEditController*db_entity_interest.db_entity'), + url: function() { + var content = this.get('content'); + return content ? + Footprint.store.dataSource.uploadUri( + content.get('store'), + content.get('storeKey')) : + '/content-not-set'; // action can handle null + }.property('content').cacheable(), + // bending over backwards to proxy isUploading to somewhere central + isUploadingBinding: SC.Binding.oneWay('.form.isUploading'), // this should be in the view class, not here. + isUploadingDidChange: function() { + this.setPathIfChanged('pane.layerFileIsUploading', this.get('isUploading')); + }.observes('isUploading'), + resultDidUpdate: function() { + var result = this.get('result'); + // We need to know the extension of the submitted file + if (!result) + return; + var extension = this.getPath('form.value').split('.').slice(-1)[0]; + this.setPath('content.upload_id', '%@.%@'.fmt(result.upload_id, extension)); + this.reset(); + }.observes('result'), + + form: SC.UploadForm.extend({ + // Override this so that we can catch errors + iframe: SC.IFrameView.extend({ + load: function () { + try { + sc_super() + } + catch(e) { + // Clear the value so the user can try again + this.setPath('parentView.input.value', null); + SC.AlertPane.warn({ + message: "Upload failed", + description: "Only zipped shape files are supported. Contact us if something else is wrong." + }); + } + this.setPath('parentView.isUploading', NO); + } + }), + submitOnChange: SC.outlet('parentView.submitOnChange'), + inputName: SC.outlet('parentView.inputName') + }) + }) + }), + + createFromLayerSelectionView: Footprint.CheckboxInfoView.extend({ + classNames: 'footprint-layer-info-create-from-layer-selection-view'.w(), + layout: { left: 0, bottom: 20, height: 20 }, + buttonLayout: { left: 0 }, + titleLayout: { left: 15 }, + isVisibleBinding: SC.Binding.and('.parentView.parentView.contentIsNew', 'Footprint.layerEditController.origin_instance'), + title: 'Limit to current features of source layer selection', + valueBinding: 'Footprint.layerEditController.create_from_selection' + }) + }), + + buttonViews: SC.View.extend({ + layout: { bottom: 0, height: 30, left: 245, right: 20 }, + childViews: ['newButtonView', 'cancelButtonView', 'saveButtonView'], + content: null, + contentBinding: SC.Binding.oneWay('Footprint.layerEditController.content'), + + newButtonView: SC.ButtonView.extend({ + layout: { bottom: 5, left: 0, height: 24, width: 95 }, + title: 'New Layer', + icon: 'add-icon', + action: 'doCreateLayer' + }), + cancelButtonView: Footprint.CancelButtonView.extend({ + layout: { bottom: 5, right: 130, height: 24, width: 80 }, + calculatedStatusBinding: SC.Binding.oneWay('Footprint.layersEditController.calculatedStatus') + }), + saveButtonView: SC.ButtonView.extend({ + layout: { bottom: 5, right: 0, height: 24, width: 120 }, + + uploadId: null, + uploadIdBinding: SC.Binding.oneWay('Footprint.layerEditController*db_entity_interest.db_entity.upload_id'), + originInstance: null, + originInstanceBinding: SC.Binding.oneWay('Footprint.layerEditController.origin_instance'), + contentIsNew: null, + contentIsNewBinding: SC.Binding.oneWay('.parentView.parentView.contentIsNew'), + isEnabled: function() { + // Enable saves for new content if an uploadId exists or its a clone + // Enable saves for all existing content + return !this.get('contentIsNew') || (this.get('uploadId')!=null || this.get('originInstance')); + }.property('contentIsNew', 'uploadId', 'originInstance').cacheable(), + + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + title: 'Save All Changes', + action: 'doSave' + }) + }), + + uploadingOverlay: SC.View.extend({ + classNames: ['form-info-overlay'], + isVisibleBinding: SC.Binding.oneWay('.pane.layerFileIsUploading'), + childViews: ['labelView'], + labelView: SC.LabelView.extend({ + layout: { height: 20, width: 250, centerX: 0, centerY: 0 }, + value: 'Uploading...' + }) + }) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/management/analytic_model_management_view.js b/sproutcore/apps/fp/views/info_views/management/analytic_model_management_view.js new file mode 100644 index 000000000..65f805aee --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/management/analytic_model_management_view.js @@ -0,0 +1,34 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 11/14/13 + * Time: 3:54 PM + * To change this template use File | Settings | File Templates. + */ + +sc_require('views/info_views/analytics/fiscal_model_management_view'); + + +Footprint.analyticModelManagementView = SC.View.extend({ + classNames: "footprint-analytic-model-management-view".w(), + childViews: 'contentView'.w(), + contentView: SC.SegmentedView.extend({ + layout: { top:.02, height: 0.98, right: 4, width: 110 }, + layoutDirection: SC.LAYOUT_VERTICAL, + selectSegmentWhenTriggeringAction: YES, + itemTitleKey: 'title', + itemValueKey: 'value', + items: [ + {title: 'Travel', value: ''}, + {title: 'Fiscal', value: 'Footprint.fiscalModelManagementView'}, + {title: 'Water', value: ''}, + {title: 'Building Energy', value: ''}, + {title: 'Emissions', value: ''}, + {title: 'Public Health', value: ''} + ], + nowShowingBinding: '.parentView.nowShowing' + + + }) + +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/management/compare_scenarios_management_view.js b/sproutcore/apps/fp/views/info_views/management/compare_scenarios_management_view.js new file mode 100644 index 000000000..0042fcf7d --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/management/compare_scenarios_management_view.js @@ -0,0 +1,12 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 11/14/13 + * Time: 3:54 PM + * To change this template use File | Settings | File Templates. + */ + + +Footprint.compareScenariosManagementView = SC.View.extend({ + +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/management/scenario_development_management_view.js b/sproutcore/apps/fp/views/info_views/management/scenario_development_management_view.js new file mode 100644 index 000000000..6816ad337 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/management/scenario_development_management_view.js @@ -0,0 +1,206 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 11/14/13 + * Time: 3:48 PM + * To change this template use File | Settings | File Templates. + */ + + +Footprint.scenarioDevelopmentManagementView = SC.View.extend({ + classNames: "footprint-scenario-development-management-view".w(), + childViews: 'contentView reportHighLevelView reportResidentialLowLevelView reportEmploymentLowLevelView'.w(), + contentView: SC.SegmentedView.extend({ + layout: { top:.02, height: 0.98, left: 4, width: 80 }, + layoutDirection: SC.LAYOUT_VERTICAL, + selectSegmentWhenTriggeringAction: YES, + itemTitleKey: 'title', + itemValueKey: 'value', + items: [ + {title: 'Base Year', value: ''}, + {title: 'End State', value: ''}, + {title: 'Increments', value: ''} + ] + }), + + reportHighLevelView: SC.View.extend({ + layout: {top: 16, width: 400, height: 60, left: 85}, + childViews: 'dwellingUnitTitleView dwellingUnitSummaryView employmentTitleView employmentSummaryView'.w(), + classNames: "footprint-report-high-level-values-view".w(), + + dwellingUnitTitleView: SC.LabelView.design({ + classNames: "footprint-dwelling-unit-label-view".w(), + value: 'Dwelling Units', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + layout: {left: 55, width: 85, height: 16} + + }), + dwellingUnitSummaryView: SC.LabelView.extend({ + classNames: "footprint-dwelling-unit-summary-view".w(), + + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 18, left: 55, width: 85, height: 30} +// recordType:null, +// recordTypeBinding:Footprint.Feature, +// contentBinding: SC.Binding.oneWay('Footprint.featuresActiveDbEntityKeysController.content'), +// selectionBinding: SC.Binding.oneWay('Footprint.featuresActiveDbEntityKeysController') + + }), + + employmentTitleView: SC.LabelView.extend({ + classNames: "footprint-employment-label-view".w(), + value: 'Employees', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + layout: {left: 250, width: 85, height: 16} + + }), + employmentSummaryView: SC.LabelView.extend({ + classNames: "footprint-employment-summary-view".w(), + value: '5', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 18, left: 250, width: 85, height: 30} + }) + }), + + reportResidentialLowLevelView: SC.View.extend({ + layout: {top: 72, bottom: 2, left: 90, width: 200}, + classNames: "footprint-report-residential-low-level-values-view".w(), + childViews: 'singleFamilySmallTitleView singleFamilySmallSummaryView singleFamilyLargeTitleView singleFamilyLargeSummaryView attachedTitleView attachedUnitSummaryView multifamilyUnitTitleView multifamilyUnitSummaryView'.w(), + + singleFamilySmallTitleView: SC.LabelView.design({ + classNames: "footprint-single-family-small-title-view".w(), + value: 'Single Family Small Lot: ', + layout: {top: 2, left: 10, width: 130, height: 16} + + }), + singleFamilySmallSummaryView: SC.LabelView.extend({ + classNames: "footprint-single-family-small-summary-view".w(), + value: '5', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 2, left: 140, width: 50, height: 16} + + }), + singleFamilyLargeTitleView: SC.LabelView.extend({ + classNames: "footprint-single-family-large-title-view".w(), + value: 'Single Family Large Lot:', + layout: {top: 25, left: 10, width: 130, height: 16} + + }), + singleFamilyLargeSummaryView: SC.LabelView.extend({ + classNames: "footprint-single-family-large-summary-view".w(), + value: '5', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 25, left: 140, width: 50, height: 16} + + }), + attachedTitleView: SC.LabelView.extend({ + classNames: "footprint-attached-title-view".w(), + value: 'Attached Single Family:', + layout: {top: 48, left: 10, width: 130, height: 16} + + }), + attachedUnitSummaryView: SC.LabelView.extend({ + classNames: "footprint-attached-summary-view".w(), + value: '5', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 48, left: 140, width: 50, height: 16} + + }), + multifamilyUnitTitleView: SC.LabelView.extend({ + classNames: "footprint-multifamily-title-view".w(), + value: 'Multifamily:', + layout: {top: 71, left: 10, width: 130, height: 16} + + }), + multifamilyUnitSummaryView: SC.LabelView.extend({ + classNames: "footprint-multifamily-summary-view".w(), + value: '5', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 71, left: 140, width: 50, height: 16} + + }) + }), + + reportEmploymentLowLevelView: SC.View.extend({ + layout: {top: 72, bottom: 2, left: 328, width: 150}, + classNames: "footprint-report-employment-low-level-values-view".w(), + childViews: 'retailTitleView retailSummaryView officeTitleView officeSummaryView publicTitleView publicSummaryView industrialTitleView industrialSummaryView'.w(), + + retailTitleView: SC.LabelView.design({ + classNames: "footprint-retail-title-view".w(), + value: 'Retail: ', + layout: {top: 2, left: 10, width: 60, height: 16} + + }), + retailSummaryView: SC.LabelView.extend({ + classNames: "footprint-retail-summary-view".w(), + value: '5', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 2, left: 70, width: 50, height: 16} + + }), + officeTitleView: SC.LabelView.extend({ + classNames: "footprint-office-title-view".w(), + value: 'Office:', + layout: {top: 25, left: 10, width: 60, height: 16} + + }), + officeSummaryView: SC.LabelView.extend({ + classNames: "footprint-office-summary-view".w(), + value: '5', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 25, left: 70, width: 50, height: 16} + + }), + publicTitleView: SC.LabelView.extend({ + classNames: "footprint-public-title-view".w(), + value: 'Public:', + layout: {top: 48, left: 10, width: 60, height: 16} + + }), + publicSummaryView: SC.LabelView.extend({ + classNames: "footprint-public-summary-view".w(), + value: '5', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 48, left: 70, width: 50, height: 16} + + }), + industrialTitleView: SC.LabelView.extend({ + classNames: "footprint-industrial-title-view".w(), + value: 'Industrial:', + layout: {top: 71, left: 10, width: 60, height: 16} + + }), + industrialSummaryView: SC.LabelView.extend({ + classNames: "footprint-industrial-summary-view".w(), + value: '5', + textAlign: SC.ALIGN_CENTER, + fontWeight: 700, + backgroundColor: '#F8F8F8', + layout: {top: 71, left: 70, width: 50, height: 16} + + }) + + }) + +}) \ No newline at end of file diff --git a/sproutcore/apps/fp/views/info_views/medium_info_view.js b/sproutcore/apps/fp/views/info_views/medium_info_view.js new file mode 100644 index 000000000..a35347375 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/medium_info_view.js @@ -0,0 +1,39 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/info_views/key_info_view'); + +Footprint.MediumInfoView = Footprint.SelectUpdateInfoView.extend({ + classNames:'footprint-medium-info-view'.w(), + childViews:'titleView contentView selectView'.w(), + title: 'Medium', + contentView:Footprint.NameInfoView.extend({ + layout: {left: .2, width: .8}, + contentBinding: SC.Binding.oneWay(parentViewPath(1,'*content.medium')), + itemsBinding: SC.Binding.oneWay(parentViewPath(1,'*items')) + }) +}); + +/** + * Edits Medium instances who define one or more CSS-style colors + * @type {SC.RangeObserver} + */ +Footprint.MediumColorsInfoView = Footprint.MediumInfoView.extend({ + contentView:Footprint.NameInfoView.extend({ + layout: {left: .2, width: .8}, + // The instance can't be changed in the controller + contentBinding: SC.Binding.oneWay(parentViewPath(2,'*content.medium')), + itemsBinding: SC.Binding.oneWay(parentViewPath(1,'*items')) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/policy/policy_info_view.js b/sproutcore/apps/fp/views/info_views/policy/policy_info_view.js new file mode 100644 index 000000000..c9b88fc51 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/policy/policy_info_view.js @@ -0,0 +1,69 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/geography_info_view'); +sc_require('views/info_views/tags_info_view'); +sc_require('views/info_views/medium_info_view'); +sc_require('views/sections/analytic_section_view'); + + + +/*** + * The pane used to edit the settings of a new or existing Policy and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the Policy if a DbEntity is being created here + * @type {*} + */ +Footprint.PolicyInfoView = Footprint.InfoPane.extend({ + classNames:'footprint-policy-info-view'.w(), + layout: { top:0, left: 0, width:400, height:400 }, + + recordType: Footprint.Policy, + refreshTarget: Footprint.AnalyticSectionView, + controllers: Footprint.policyControllers, + + contentView: SC.View.extend({ + childViews:['nameView', 'keyView', 'descriptionView', 'valueView', 'tagsView', 'commitButtonsView'], + + nameView: Footprint.NameInfoView.extend({ + useStaticLayout:YES, + layout: {width: .9, height: .05} + }), + keyView: Footprint.KeyInfoView.extend({ + useStaticLayout:YES, + layout: {width: .9, height: .1} + }), + descriptionView: Footprint.DescriptionItemView.extend({ + useStaticLayout:YES, + layout: {width: .9, height: .1} + }), + valueView: Footprint.InfoView.extend({ + useStaticLayout:YES, + layout: {width: .9, height: .08}, + + title: 'Value', + contentView: Footprint.EditableModelStringView.extend({ + layout: {left: .2, width: .8}, + valueBinding: parentViewPath(2,'*content.value') + }) + }), + tagsView: Footprint.TagsInfoView.extend({ + useStaticLayout:YES, + layout: {width: .7, height: .1} + }), + commitButtonsView: Footprint.InfoPaneButtonsView.extend({ + useStaticLayout:YES, + layout: {width: .9, height: .1} + }) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/policy/policy_set_info_view.js b/sproutcore/apps/fp/views/info_views/policy/policy_set_info_view.js new file mode 100644 index 000000000..d9fe6e39b --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/policy/policy_set_info_view.js @@ -0,0 +1,54 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/geography_info_view'); +sc_require('views/info_views/tags_info_view'); +sc_require('views/info_views/medium_info_view'); +sc_require('views/section_toolbars/policy_toolbar_view'); + + +/*** + * The pane used to edit the settings of a new or existing PolicySet and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the PolicySet if a DbEntity is being created here + * @type {*} + */ +Footprint.PolicySetInfoView = Footprint.InfoPane.extend({ + classNames: 'footprint-policy-set-info-view'.w(), + layout: { top: 0, left: 0, width: 400, height: 400 }, + + recordType: Footprint.PolicySet, + controllers: Footprint.policySetControllers, + + contentView: SC.View.extend({ + childViews: ['nameView', 'keyView', 'descriptionView', 'commitView'], + + nameView: Footprint.NameInfoView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .05} + }), + keyView: Footprint.KeyInfoView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .1} + }), + descriptionView: Footprint.DescriptionItemView.extend({ + useStaticLayout: YES, + contentType: 'editController.contentType', + layout: {width: .9, height: .1} + }), + commitButtonsView: Footprint.InfoPaneButtonsView.extend({ + useStaticLayout: YES, + layout: {width: .9, height: .1} + }) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_export_view.js b/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_export_view.js new file mode 100644 index 000000000..52779b25d --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_export_view.js @@ -0,0 +1,27 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +/*** + * The pane used to export PresentationMedia that are a combination of a DbEntity and a medium (such as a style) + * @type {*} + */ +Footprint.PresentationMediumExportView = SC.PickerPane.extend({ + classNames:'footprint-presentation-medium-export-view'.w(), + contentView: SC.View.extend({ + childViews:['saveDialog'], + + // TODO Save dialog with export type and output location + saveDialog: SC.View.extend() + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_import_view.js b/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_import_view.js new file mode 100644 index 000000000..c2c021cd2 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_import_view.js @@ -0,0 +1,39 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/info_views/key_info_view'); + +/*** + * The pane used to import a PresentationMedium or at least a DbEntity (the former is a combination of a DbEntity and medium, such as a style file) + * @type {*} + */ +Footprint.PresentationMediumImportView = SC.PickerPane.extend({ + classNames:'footprint-presentation-medium-import-view'.w(), + contentView: SC.View.extend({ + childViews:['fileUpload', 'uri', 'ownerSelect', 'keySelect', 'errorMessage'], + fileUpload: SC.View.extend(), + // The query for the DbEntity if relevant (if the DbEntity represents a query or a database view) + uri: Footprint.EditableModelStringView.extend({ + valueBinding: 'Footprint.presentationMediumEditController.dbEntity.name' + }), + ownerSelect: Footprint.LabelSelectView.extend({ + itemTitleKey: 'name' + // What ConfigEntity context will own the DbEntity (Scenario or Project) + }), + keySelect: Footprint.KeyInfoView.extend({ + valueBinding: 'Footprint.presentationMediumEditController.dbEntity' + }), + errorMessage: SC.View.extend() + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_info_view.js b/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_info_view.js new file mode 100644 index 000000000..6fcfdbb05 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_info_view.js @@ -0,0 +1,103 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + +sc_require('views/info_views/geography_info_view'); +sc_require('views/info_views/tags_info_view'); +sc_require('views/info_views/medium_info_view'); +sc_require('views/info_views/query_info_view'); +sc_require('views/info_views/key_info_view'); +sc_require('views/info_views/presentation/style_info_view'); + + +/*** + * The pane used to edit the settings of a new or existing PresentationMedium and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the PresentationMedium if a DbEntity is being created here + * @type {*} + */ +Footprint.PresentationMediumInfoView = Footprint.InfoPane.extend({ + classNames:'footprint-presentation-medium-info-view'.w(), + layout: { top:0, left: 0, width:400, height:400 }, + + recordType: Footprint.PresentationMedium, + refreshTarget: Footprint.LayerSectionView, + controllers: Footprint.layerControllers, + + contentView: SC.ScrollView.extend({ // Creates an intermediate view as well + + contentView: Footprint.ContentView.extend({ + childViews:['nameView', 'tableView', 'queryView', 'keyView', 'descriptionView', 'geographyView', 'tagsView', 'styleView', 'commitButtonsView'], + + // The content of the view is the PresentationMedium + contentBinding: parentViewPath(3, '.editController'), + // Various collections not specific to the content needed by child views + contextBinding: parentViewPath(3, '.recordSetController'), + + nameView: Footprint.NameInfoView.extend({ + layout: {width: .9, height: .05, top: 0}, + contentBinding: parentViewPath(1, '*content.db_entity_interest.db_entity') + }), + + // The query for the DbEntity if relevant (if the DbEntity represents a query or a database view) + queryView: Footprint.QueryInfoView.extend({ + layout: {width: .9, height: .2, top: .1}, + contentBinding: parentViewPath(1, '*content.db_entity_interest.db_entity') + }), + + keyView: Footprint.KeyInfoView.extend({ + layout: {width: .9, height: .05, top: .3}, + keyItemPath:'key', + contentBinding: parentViewPath(1, '*content.db_entity_interest.db_entity') + }), + + descriptionView: Footprint.DescriptionItemView.extend({ + layout: {width: .9, height: .1, top: .35}, + contentBinding: parentViewPath(1, '*content.db_entity_interest.db_entity') + }), + + geographyView: Footprint.GeographyInfoView.extend({ + layout: {width: .9, height: .05, top: .45}, + contentBinding: parentViewPath(1, '*content.db_entity_interest.db_entity') + }), + + tagsView: Footprint.TagsInfoView.extend({ + layout: {width: .9, height: .05, top: .5}, + contentBinding: parentViewPath(1, '*content.db_entity_interest.db_entity') + }), + + styleView: Footprint.StyleInfoView.extend({ + layout: {width: .9, height: .4, top: .55}, + // Present other PresentationMedium instances of the Presentation in a select box so that + // their style information might be copied. + // TODO this actually needs to be other instances of the same table and thus the same attributes + // or a way to copy over individual attribute styles + itemsBinding: SC.Binding.oneWay(parentViewPath(1, '*context.items')), + // With the PresentationMedium instance as the content, we want to be able to copy the medium_context + // attributes from the selected value in the select box to the content. + objectPath: '.medium_context' + }), + + commitButtonsView: Footprint.InfoPaneButtonsView.extend({ + layout: {width: .9, height: .05, top: .95} + }) + }) + }) +}); + +Footprint.LayerInfoView = Footprint.PresentationMediumInfoView.extend({ + refreshTarget: Footprint.PresentationMediumInfoView +}); + +Footprint.ResultInfoView = Footprint.PresentationMediumInfoView.extend({ + refreshTarget: Footprint.ResultInfoView +}); diff --git a/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_remove_view.js b/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_remove_view.js new file mode 100644 index 000000000..b12510923 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/presentation/presentation_medium_remove_view.js @@ -0,0 +1,36 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + * + */ + + +/*** + * The pane used to edit the settings of a new or existing PresentationMedium and the DbEntity to which it is associated (if any). The saving order of this will have to first save a created DbEntity and then the PresentationMedium if a DbEntity is being created here + * @type {*} + */ +Footprint.PresentationMediumRemoveView = SC.PickerPane.extend({ + classNames:'footprint-presentation-medium-remove-view'.w(), + layout:{width:200, height:200}, + // Use Browser positioning + useStaticLayout:YES, + contentView: SC.View.extend({ + childViews:['nameView', 'deleteView'], + nameView: Footprint.EditableModelStringView.extend({ + valueBinding: 'Footprint.presentationMediumEditController.dbEntity.name' + }), + // The query for the DbEntity if relevant (if the DbEntity represents a query or a database view) + deleteView: Footprint.EditableModelStringView.extend({ + + }) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/presentation/style_info_view.js b/sproutcore/apps/fp/views/info_views/presentation/style_info_view.js new file mode 100644 index 000000000..f6d34d6d9 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/presentation/style_info_view.js @@ -0,0 +1,33 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/info_views/medium_info_view'); +sc_require('views/info_views/key_info_view'); + +/*** + * Edits the style data stored in a PresentationMedium.medium_context or similar + * Set the content to to + * A SelectView is populated via the items property to provide a way to copy values from an existing style. + * The items must be the same type as the content. + * @type {*} + */ +Footprint.StyleInfoView = Footprint.SelectUpdateInfoView.extend({ + classNames: "footprint-style-info-view".w(), + title: 'Style', + + contentView:Footprint.InfoView.extend({ + layout: {left: .2, width: .8}, + contentBinding: SC.Binding.oneWay(parentViewPath(1,'*content')) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/query_info_view.js b/sproutcore/apps/fp/views/info_views/query_info_view.js new file mode 100644 index 000000000..67515583e --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/query_info_view.js @@ -0,0 +1,191 @@ + +/* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +sc_require('views/clear_button_view'); + +// TODO this will become more complicated to handle separate group bys +Footprint.QueryInfoView = Footprint.InfoView.extend({ + classNames:'footprint-query-info-view'.w(), + recordType: null, + layout: {left: 0.01, right: 0.01, top: 5, height: 24}, + + layerName: null, + layerNameBinding: SC.Binding.oneWay('Footprint.layerActiveController.name'), + title: function() { + return 'Select features from %@'.fmt(this.get('layerName')); + }.property('layerName'), + isEnabledBinding: SC.Binding.oneWay('.content.status').matchesStatus(SC.Record.READY), + + // Toggle off to hide the summary fields (e.g. group by) + showSummaryFields: YES, + + titleView: SC.LabelView.extend({ + classNames: "footprint-infoview-title-view".w(), + layout: {left: 0.01, height: 24}, + valueBinding: '.parentView.title' + }), + + contentView: SC.View.extend({ + classNames:'footprint-query-info-content-view'.w(), + layout: {left: 0.01, right: 0.01, top: 26}, + childViews:'boundsView filterView joinView joinColumnsView queryButtonView aggregatesView groupByView'.w(), + contentBinding: '.parentView.content', + showSummaryFieldsBinding: SC.Binding.oneWay('.parentView.showSummaryFields'), + + // Read-only view of selected bounds + boundsView: Footprint.InfoView.extend({ + childViews:'titleView contentView clearButtonView'.w(), + classNames:'footprint-query-info-bounds-view'.w(), + layout: {width:0.48, top:0, height: 24}, + title: 'Bounds:', + contentBinding: '.parentView*content', + titleView: SC.LabelView.extend({ + classNames: "footprint-infoview-title-view".w(), + layout: {left: 0.01, height: 24, width: 0.1}, + valueBinding: '.parentView.title', + needsEllipsis: YES + }), + contentView: SC.LabelView.extend({ + layout: {left: 0.12, right: 0.1}, + contentBinding: '.parentView.content', + contentValueKey: 'boundsAsString', + hint: 'No bounds selected. Drag bounds on the map to filter by bounds' + }), + clearButtonView: Footprint.ClearButtonView.extend({ + layout: {left: 0.9, width: 24}, + isVisibleBinding: SC.Binding.oneWay('.parentView*content.bounds').transform(function(bounds) { + return bounds && bounds.getPath('coordinates'); + }).bool(), + action: 'doClearBounds' + }) + }), + + filterView: Footprint.InfoView.extend({ + childViews:'titleView contentView clearButtonView'.w(), + classNames:'footprint-query-info-content-view'.w(), + layout: {width:0.48, top:26, height: 24}, + title: 'Where:', + contentBinding: '.parentView*content.query_strings', + titleView: SC.LabelView.extend({ + classNames: "footprint-infoview-title-view".w(), + layout: {left: 0.01, height: 24, width: 0.1}, + valueBinding: '.parentView.title' + }), + contentView: Footprint.EditableModelStringView.extend({ + layout: {left: 0.12, right: 0.1}, + contentBinding: '.parentView.content', + contentValueKey: 'filter_string' + }), + clearButtonView: Footprint.ClearButtonView.extend({ + layout: {left: 0.9, width: 24}, + isVisibleBinding: SC.Binding.oneWay('.parentView*content.filter_string').bool(), + action: 'doClearFilter' + }) + }), + + joinView: Footprint.SelectInfoView.extend({ + classNames:'footprint-query-info-join-view'.w(), + layout: {left: 0.02, width:0.42, top:53, height: 24}, + title: 'Join:', + recordType:null, + recordTypeBinding:SC.Binding.oneWay('.parentView.recordType'), + contentBinding: SC.Binding.oneWay('Footprint.joinLayersController.content'), + selectionBinding: 'Footprint.joinLayersController.selection', + itemTitleKey:'db_entity_key', + includeNullItem:YES, + nullTitle: 'None', + titleView: SC.LabelView.extend({ + classNames: "footprint-infoview-title-view".w(), + layout: {left: 0.01, height: 24, width: 0.1}, + valueBinding: '.parentView.title' + }) + }), + + /*** + * Displays the join columns available from the selected join. + * This will go away when better auto-complete is up and running (as will the join) + */ + joinColumnsView: Footprint.SelectInfoView.extend({ + classNames:'footprint-query-info-join-columns-view'.w(), + layout: {left: 0.4, width:0.5, top:53, height: 24}, + title: '', + recordType:null, + recordTypeBinding:SC.Binding.oneWay('.parentView.recordType'), + contentBinding: SC.Binding.oneWay('Footprint.availableFieldsController.content'), + selectionBinding: 'Footprint.availableFieldsController.selection', + maxHeight: 300, + // Don't let the user select anything + isSelectable: NO, + titleView: SC.LabelView.extend({ + classNames: "footprint-infoview-title-view".w(), + layout: {left: 0.01, height: 24, width: 0.1}, + valueBinding: '.parentView.title' + }), + nullTitle: 'View available fields', + // Primitives get their title from the content + itemTitleView: 'content' + }), + + queryButtonView: SC.ButtonView.design({ + classNames: ['footprint-query-info-query-button-view', 'theme-button', 'theme-button-green'], + layout: {left:0.49, width:0.1, top:13, height: 24}, + title: 'Query', + action: 'doExecuteQuery', + // We pass the layerSelection content to the action + contentBinding: '.parentView.content', + isEnabledBinding: SC.Binding.oneWay('.content.status').matchesStatus(SC.Record.READY) + }), + + aggregatesView: Footprint.InfoView.extend({ + childViews:'titleView contentView clearButtonView'.w(), + classNames:'footprint-query-info-aggregates-view'.w(), + layout: {left:0.62, top:0, height: 24}, + title: 'Aggregates', + hint: 'Enter aggregates (e.g. SUM(du), AVG(emp))', + isVisibleBinding: SC.Binding.oneWay('.parentView.showSummaryFields'), + contentBinding: '.parentView*content.query_strings', + contentView: Footprint.EditableModelStringView.extend({ + layout: {left: 0.2, right:0.1}, + contentBinding: '.parentView.content', + contentValueKey: 'aggregates_string' + }), + clearButtonView: Footprint.ClearButtonView.extend({ + layout: {left: 0.9, width: 24}, + isVisibleBinding: SC.Binding.oneWay('.parentView*content.aggregates_string').bool(), + action: 'doClearAggregates' + }) + }), + + groupByView: Footprint.InfoView.extend({ + childViews:'titleView contentView clearButtonView'.w(), + classNames:'footprint-query-info-group-by-view'.w(), + layout: {left:0.62, top:26, height: 24}, + title: 'Group By', + hint: 'Enter group-by columns of main or join table (e.g. blockgroup)', + contentBinding: '.parentView*content.query_strings', + isVisibleBinding: SC.Binding.oneWay('.parentView.showSummaryFields'), + contentView: Footprint.EditableModelStringView.extend({ + layout: {left: 0.2, right:0.1}, + contentBinding: '.parentView.content', + contentValueKey: 'group_by_string' + }), + clearButtonView: Footprint.ClearButtonView.extend({ + layout: {left: 0.9, width: 24}, + isVisibleBinding: SC.Binding.oneWay('.parentView*content.group_by_string').bool(), + action: 'doClearGroupBy' + }) + }) + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/tags_info_view.js b/sproutcore/apps/fp/views/info_views/tags_info_view.js new file mode 100644 index 000000000..52dd6b4ba --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/tags_info_view.js @@ -0,0 +1,25 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + + +Footprint.TagsInfoView = Footprint.InfoView.extend({ + classNames: "footprint-tags-info-view".w(), + title: 'Tags', + contentView: Footprint.LabelSelectView.extend({ + contentBinding: parentViewPath(2, '.content'), + selectedItemBinding: '.parentView.parentView*controller.selection.firstObject', + itemsBinding: parentViewPath(3,'*recordSetController.keyObjects'), + valuesBinding: parentViewPath(2,'*content.tags') + }) +}); diff --git a/sproutcore/apps/fp/views/info_views/vis_pane_views/bar_chart_view.js b/sproutcore/apps/fp/views/info_views/vis_pane_views/bar_chart_view.js new file mode 100644 index 000000000..607edc632 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/vis_pane_views/bar_chart_view.js @@ -0,0 +1,261 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 9/16/13 + * Time: 2:20 PM + * To change this template use File | Settings | File Templates. + */ +Footprint.BarChartView = SC.View.extend({ + layout: { top: 0.06, left: 0.35, right: 0.05, height: 0.20}, + classNames: ['barChart'], + displayProperties: ['content'], + + content: null, + + color: null, + colorBinding: SC.Binding.oneWay('.parentView.color'), + + backgroundColor: null, + backgroundColorBinding: SC.Binding.oneWay('.parentView.color'), + + classNameBindings: ['hasLightBackground'], + + //this class is assigned when the background color is light enough that the text on top should be black + // isLightColor uses the color code to make the light/dark determination + hasLightBackground: function(){ + + if (this.get('color')) { + return isLightColor(this.get('color')); + } + + }.property('color').cacheable(), + + pt_score: function() { + return this.getPath('content.pt_score'); + }.property('content').cacheable(), + + // one piece of data for each of the bars in graph + barData: function() { + return barData = [ + { "title": "Density", + "score" : this.getPath('content.pt_density') }, + { "title" : "Connectivity", + "score" : this.getPath('content.pt_connectivity') }, + { "title" : "Mix of Use", + "score" : this.getPath('content.pt_land_use_mix') } + ]; + }.property('content').cacheable(), + + render: function(context) { + sc_super(); + }, + + update: function(context) { + + if (this.getPath('content.status') & SC.Record.READY) { + + var _data = this.get('barData'); + + var svgWidth = this.getPath('frame.width'), + svgHeight = this.getPath('frame.height'), + chartW = svgWidth*0.6, + chartH = svgHeight*0.6, + labelWidth = chartW*3/10; + + // Expects that the placetype score will be a value between 0 and 10 + var xScale = d3.scale.linear() + .domain([0, 10]) + .range([0, chartW]); + + var yScale = d3.scale.ordinal() + .domain(_data.map(function (d) {return d.title; })) + .rangeRoundBands([0, chartH]); + + var xAxis = d3.svg.axis() + .scale(xScale) + .orient("bottom"); + + var yAxis = d3.svg.axis() + .scale(yScale) + .orient("left"); + + var svg = d3.selectAll(context) + .selectAll("svg") + .data([_data]); + + var container = svg.enter().append("svg") + .attr("width", svgWidth) + .attr("height", svgHeight) + .append("g").classed("container-group", true); + + container.append("g").attr("class", "chart-background"); + container.append("g").attr("class", "chart-group"); + container.append("g").attr("class", "y-axis"); + container.append("g").attr("class", "x-axis"); + container.append("g").attr("id","score"); + + var barGradient = container.append("linearGradient") + .attr("x1", 0) + .attr("x2", xScale(10)) + .attr("id", "barGradient") + .attr("gradientUnits", "userSpaceOnUse"); + + barGradient.append("stop") + .attr("offset", "0") + .attr("stop-color", "#4169E1"); + + barGradient.append("stop") + .attr("offset", "0.5") + .attr("stop-color", "#162252"); + + var backgroundGradient = container.append("linearGradient") + .attr("x1", 0) + .attr("x2", xScale(10)) + .attr("id", "backgroundGradient") + .attr("gradientUnits", "userSpaceOnUse"); + + backgroundGradient + .append("stop") + .attr("offset", "0") + .attr("stop-opacity", "0.5") + .attr("stop-color", "#FFFFFF"); + + backgroundGradient + .append("stop") + .attr("offset", "0.5") + .attr("stop-color", "#FFFFFF") + .attr("stop-opacity", "0"); + + container.select(".chart-background") + .append("rect") + .attr('width', xScale(10)) + .attr('height', chartH) + .attr('fill', "url(#backgroundGradient)"); + + var bars = svg.select(".chart-group") + .selectAll(".bar") + .data(_data); + bars.enter().append("g"); + bars.attr("class","bar"); + + bars.each(function(d, i) { + var barRects = d3.select(this).selectAll('rect') + .data([d]); + + barRects.transition() + .duration(500) + .ease("linear") + .attr('y', function(d) { return yScale(d.title); }) + .attr('x', 0) + .attr('width', function(d) { return xScale(d.score); }) + .attr('height', yScale.rangeBand()) + .attr('fill', "url(#barGradient)"); + + + barRects.enter().append('rect') + .attr('y', function(d) { return yScale(d.title); }) + .attr('x', 0) + .attr('width', function(d) { return xScale(d.score); }) + .attr('height', yScale.rangeBand()) + .attr('fill', "url(#barGradient)"); + +// barRects.transition() +// .duration(500) +// .ease("linear") +// .attr('y', function(d) { return yScale(d.title); }) +// .attr('x', 0) +// .attr('width', function(d) { return xScale(d.score); }) +// .attr('height', yScale.rangeBand()) +// .attr('fill', "url(#barGradient)"); + + var barNumber = d3.select(this).selectAll('text') + .data([d]); + barNumber.enter().append('text'); + + barNumber.attr('y', function(d) { return yScale(d.title) + yScale.rangeBand()/2; }) + .attr('x', function(d) { return xScale(d.score); }) + .attr('dy', "0.35em") //vertical-align middle + .attr('dx', "-1em") + .attr('text-anchor', 'end') + .style('fill', 'white'); + barNumber.text(function(d) { + if (d.score != 0) { + return d.score; + }else return "";}); + }); + + container.select(".x-axis") + .attr("stroke-width", 1) + .attr("transform", "translate(0," + chartH + ")") + .call(xAxis); + + container.select(".x-axis path") + .attr("display", "none"); + + container.select(".y-axis") + .attr("transform", "translate(0, 0)") + .call(yAxis); + + container.select(".y-axis path") + .attr("display", "none"); + + var scoreBox = container.select("#score") + + var scoreBoxRect = d3.select("#score").selectAll("#scoreBoxRect") + .data([0]) + scoreBoxRect.enter().append("rect") + .attr("title", "Some info about PT score") + .attr("id", "scoreBoxRect"); + scoreBoxRect.attr("x", xScale(10)) + .attr("y", 0) + .attr("width", chartH) + .attr("height", chartH) + .attr("stroke-width", 3) + .attr("fill-opacity", 0) + .attr("stroke", function() { + return (context.hasClass('has-light-background')) ? 'black' : 'white'; + }); + + var scoreBoxNum = d3.select("#score").selectAll("#number") + .data([0]) + scoreBoxNum.enter().append("text") + .attr("id", "number"); + + scoreBoxNum.text(this.get('pt_score')) + .attr("fill", function() { + return (context.hasClass('has-light-background')) ? 'black' : 'white'; + }) + .attr("dy", ".35em") //vertical-align middle + .style("font-size", function() { return chartH*.75; }) + .attr("text-anchor", "middle") + .attr("x", xScale(10) + chartH/2) + .attr("y", chartH/2); + + var scoreBoxText = d3.select("#score").selectAll("#scoreBoxText") + .data([0]) + scoreBoxText.enter().append("text") + .attr("id", "scoreBoxText") + scoreBoxText.text("PT SCORE") + .attr("fill", function() { + return (context.hasClass('has-light-background')) ? 'black' : 'white'; + }) + .attr("id", "scoreBoxText") + .attr("text-anchor", "middle") + .attr("x", xScale(10) + chartH/2) + .attr("y", -3); + + // var horizontalShift = labelWidth + ($(this).width() - ) + container.attr("transform", "translate(" + (labelWidth*1.1) + "," + chartH *0.2 + ")"); + } + }, + sidewaysBarChart: function module() { + + function exports(_selection) { + _selection.each(function(_data) { + + }); + } + return exports; + } +}); + diff --git a/sproutcore/apps/fp/views/info_views/vis_pane_views/development_chars_view.js b/sproutcore/apps/fp/views/info_views/vis_pane_views/development_chars_view.js new file mode 100644 index 000000000..0aae2f3cf --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/vis_pane_views/development_chars_view.js @@ -0,0 +1,228 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 9/16/13 + * Time: 2:20 PM + * To change this template use File | Settings | File Templates. + */ +Footprint.DevelopmentCharsView = SC.View.extend({ + layout: { top: 0.70, bottom: 0.05, left: 0.30, right: 0}, + childViews: ['devCharsTableView', 'devCharsTitleView'], + displayProperties: ['content'], + classNames: ['devChars'], + + content: null, + + devCharsTitleView: SC.LabelView.design({ + layout: { centerX: 0, width: 250, height: 20 }, + classNames: ['sectionTitle'], + textAlign: SC.ALIGN_CENTER, + tagName: "h1", + value: "Development Characteristics" + }), + + devCharsTableView: SC.View.design({ + layout: { top: 20, bottom: 0, width: 0.95, left: 0.025, right: 0.025}, + classNames: ['devCharsTable'], + + displayProperties: ['content'], + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + + tableData: function() { + + var gross_residential_density = parseFloat(this.getPath('content.dwelling_unit_density')).toFixed(2), + net_residential_density = (parseFloat(this.getPath('content.dwelling_unit_density'))/parseFloat(this.getPath('content.net_developable_land'))).toFixed(2), + gross_pop_density = parseFloat(this.getPath('content.population_density')).toFixed(2), + net_pop_density = (parseFloat(this.getPath('content.population_density'))/parseFloat(this.getPath('content.net_developable_land'))).toFixed(2), + gross_jobs_density = parseFloat(this.getPath('content.employment_density')).toFixed(2), + net_jobs_density = (parseFloat(this.getPath('content.employment_density'))/(this.getPath('content.net_developable_land'))).toFixed(2), + street_pattern = this.getPath('content.street_pattern'), + intersection_density = parseInt(this.getPath('content.intersections_sqmi')), +// block_size_avg_acres = parseFloat(this.getPath('content.block_size_avg_acres')).toFixed(2), + jobs_pop_ratio = (parseFloat(this.getPath('content.employment_density'))/parseFloat(this.getPath('content.population_density'))).toFixed(2); + + return developmentCharacteristics = [ + { + key: "built_form", + label: "Built form", + + values: [ + { + key: 'street_pattern', + label: "Street Pattern", + value: street_pattern + }, + { + key: 'intersection_density', + label: "Intersection Density", + value: intersection_density + "/sq mile" + }, + { + key : 'avg_block_size', + label: "Average Block Size", + value: "xxac" + }, + { + key : 'avg_building_height', + label: "Average Building Height", + value: "xxx" + } + ] + }, + { + key: "residential", + label: "Residential", + + values: [ + { + key: 'gross_res_density', + label: 'Gross Residential Density', + value: gross_residential_density + ' du/ac' + }, + { + key: 'net_res_density', + label: 'Net Residential Density', + value: net_residential_density + ' du/ac' + }, + { + key: 'gross_res_FAR', + label: 'Gross Residential FAR', + value: 'xx' + }, + { + key: 'net_res_FAR', + label: 'Net Residential FAR', + value: 'xx' + }, + { + key: 'gross_pop_density', + label: 'Gross Population Density', + value: gross_pop_density + ' du/ac' + }, + { + key: 'net_pop_density', + label: 'Net Population Density', + value: net_pop_density + ' du/ac' + } + ] + }, + { + key: 'employment', + label: 'Employment', + + values: [ + { + key: 'gross_jobs_density', + label: 'Gross Jobs Density', + value: gross_jobs_density + ' du/ac' + }, + { + key: 'net_jobs_density', + label: 'Net Jobs Density', + value: net_jobs_density + ' du/ac' + }, + { + key: 'gross_comm_FAR', + label: 'Gross Commercial FAR', + value: 'xx' + }, + { + key: 'net_comm_FAR', + label: 'Net Commercial FAR', + value: 'xx' + }, + { + key: 'job_pop_ratio', + label: 'Jobs/Population Ratio', + value: jobs_pop_ratio + } + ] + } + ]; + }.property('content').cacheable(), + + render: function(context) { + sc_super(); + }, + + + update: function(context) { + + if (this.getPath('content.status') & SC.Record.READY) { + var developmentCharacteristics = this.get('tableData'); + + + // Very important to always do a selectAll, data, enter, and append for the first element you add to the dom + // this way, each time you update, you aren't appending a new element, but rather you are replacing the data in the existing one + var table = d3.selectAll(context) + .selectAll(".outerTable") + .data([0]); + // Container is only added in the first pass, when enter() contains data + var container = table.enter().append("table") + .attr("class", "outerTable"); + + container + .append("tbody") + .append("tr") + .attr("class","rowForInnerTables"); + + var rowForInnerTables = table.select(".rowForInnerTables"); + rowForInnerTables + .selectAll(".tdForInnerTable") + .data(developmentCharacteristics) + .enter().append("td") + .attr("class", "tdForInnerTable") + .attr("valign","top"); + + + var tdForInnerTables = table.selectAll(".tdForInnerTable"); + + tdForInnerTables.each(function(d) { + d3.select(this).selectAll("table") + .data([0]) + .enter().append("table") + .attr("class","innerTable"); + + var innerTable = d3.select(this).selectAll(".innerTable"); + + var innerHead = innerTable.selectAll("thead") + .data([0]).enter().append("thead"); + innerHead.append("tr") + .append("th") + .text(d.label); + + innerTable.selectAll("tbody") + .data([0]) + .enter().append("tbody") + .attr("class", "innerBody"); + + var innerBody = innerTable.selectAll(".innerBody"); + + innerBody.selectAll("tr") + .data(d.values) + .enter().append("tr") + .attr("class", "innerRow"); + + var innerRows = innerBody.selectAll(".innerRow"); + + innerRows.each(function(d, i){ + var label = d3.select(this).selectAll(".label") + .data([d]); + label.enter().append("td") + .attr("class", "label") + .attr("title", d.value); + label.text(d.label); + + var value = d3.select(this).selectAll(".value") + .data([d]); + value.enter().append("td") + .attr("class", "value"); + value.text(d.value); + }); + }) + } + } + }) +}); + diff --git a/sproutcore/apps/fp/views/info_views/vis_pane_views/donut_charts_view.js b/sproutcore/apps/fp/views/info_views/vis_pane_views/donut_charts_view.js new file mode 100644 index 000000000..9192b6fc7 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/vis_pane_views/donut_charts_view.js @@ -0,0 +1,183 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 9/16/13 + * Time: 2:20 PM + * To change this template use File | Settings | File Templates. + */ +Footprint.DonutChartsView = SC.View.extend({ + classNames: ['donutCharts'], + layout: { top: 0.30, bottom:.30, left: 0.3, right: 0}, + + childViews: ['landUseChartAndTitleView', 'residentialChartAndTitleView', 'employmentChartAndTitleView'], + + content: null, + + landUseChartAndTitleView: SC.View.design({ + layout: { left: 0, top: 0, right: 0.66 }, + childViews: ['landUseTitleView', 'landUseChartView'], + + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + + landUseTitleView: SC.LabelView.design({ + layout: { top: 5, bottom: 5, centerX: 0, width: 150, height: 20 }, + classNames: ['sectionTitle'], + textAlign: SC.ALIGN_CENTER, + tagName: "h1", + value: "Landuse" + + }), + + landUseChartView: SC.View.design({ + classNames : ['landUseChart'], + layout: { top: 10 }, + + displayProperties: ['content'], + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + + update: function(context) { + if (this.getPath('content.status') & SC.Record.READY) { + + this._landUseCategories = []; + + var flatBuiltForm = this.get('content') ; + + var dataManager = d3.edge.dataManager(); + //dataManager contains the helper function "createDataObject" + //dataObjects represent the pieces of the pie chart, and contain "category" and "percentage" attributes + + // for each of the landUse categories within the flat built form, + // gets percentage data from API + // creates data object with "category" and "percentage" attributes, + // adds to array of landUseCategories + + this._landUseCategories.push( + dataManager.createDataObject("Residential", flatBuiltForm.get('percent_residential')), + dataManager.createDataObject("Mixed-Use", flatBuiltForm.get('percent_mixed_use')), + dataManager.createDataObject("Employment", flatBuiltForm.get('percent_employment')), + dataManager.createDataObject("Parks", flatBuiltForm.get('percent_parks')), + dataManager.createDataObject("Civic", flatBuiltForm.get('percent_civic')), + dataManager.createDataObject("Streets", flatBuiltForm.get('percent_streets')) + ); + + // d3.edge.donutChart is a function that takes a div bound with data as an argument + // we select the div that will hold the chart (using the context); bind the data (this._landUseCategories), + // and then call the donutChartMaker function + d3.selectAll(context).datum(this._landUseCategories) + .call(d3.edge.donutChart()); + } + } + }) + }), + residentialChartAndTitleView: SC.View.design({ + layout: { left: 0.33, top: 0, right: 0.33 }, + childViews: ['residentialTitleView', 'residentialChartView'], + + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + + residentialTitleView: SC.LabelView.design({ + layout: { centerX: 0, width: 150, height: 20 }, + classNames: ['sectionTitle'], + textAlign: SC.ALIGN_CENTER, + tagName: "h1", + value: "Residential" + }), + + residentialChartView: SC.View.design({ + classNames: ['residentialChart'], + layout: { top: 10 }, + + displayProperties: ['content'], + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + + update: function(context) { + if (this.getPath('content.status') & SC.Record.READY) { + + this._residentialCategories = []; + + var flatBuiltForm = this.get('content') ; + + var dataManager = d3.edge.dataManager(); + // RESIDENTIAL CHART + + var multi_fam_density = parseFloat(flatBuiltForm.get('multifamily_5_plus_density')) + parseFloat(flatBuiltForm.get('multifamily_2_to_4_density')), + townhome_density = parseFloat(flatBuiltForm.get('multifamily_2_to_4_density')), + single_fam_small_density = parseFloat(flatBuiltForm.get('single_family_small_lot_density')), + single_fam_large_density = parseFloat(flatBuiltForm.get('single_family_large_lot_density')); + + var total_res_density = multi_fam_density + townhome_density + single_fam_large_density + single_fam_small_density; + + this._residentialCategories.push( + dataManager.createDataObject("Multifamily", multi_fam_density/total_res_density), + dataManager.createDataObject("Townhome", townhome_density/total_res_density), + dataManager.createDataObject("Single Family Small Lot", single_fam_small_density/total_res_density), + dataManager.createDataObject("Single Family Large Lot", single_fam_large_density/total_res_density) + ); + + // d3.edge.donutChart is a function that takes a div bound with data as an argument + // we select the div that will hold the chart (using the context); bind the data (this._landUseCategories), + // and then call the donutChartMaker function + d3.selectAll(context).datum(this._residentialCategories) + .call(d3.edge.donutChart()); + + } + } + }) + }), + employmentChartAndTitleView: SC.View.design({ + layout: { left: 0.66, top: 0 }, + childViews: ['employmentTitleView', 'employmentChartView'], + + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + + employmentTitleView: SC.LabelView.design({ + layout: { centerX: 0, width: 150, height: 20 }, + classNames: ['sectionTitle'], + textAlign: SC.ALIGN_CENTER, + tagName: "h1", + value: "Employment" + }), + + employmentChartView: SC.View.design({ + classNames: ['employmentChart'], + layout: { top: 10 }, + + displayProperties: ['content'], + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + update: function(context) { + + if (this.getPath('content.status') & SC.Record.READY) { + this._employmentCategories = []; + + var flatBuiltForm = this.get('content') ; + if (!flatBuiltForm) + return; + + var dataManager = d3.edge.dataManager(); + + var retail_density = parseFloat(flatBuiltForm.get('retail_density')), + office_density = parseFloat(flatBuiltForm.get('office_density')), + industrial_density = parseFloat(flatBuiltForm.get('industrial_density')); + + var total_density = retail_density + office_density + industrial_density; + + this._employmentCategories.push( + dataManager.createDataObject("Retail", retail_density/total_density), + dataManager.createDataObject("Office", office_density/total_density), + dataManager.createDataObject("Industrial", industrial_density/total_density) + ); + + d3.selectAll(context).datum(this._employmentCategories) + .call(d3.edge.donutChart()); + } + } + }) + }) +}); + diff --git a/sproutcore/apps/fp/views/info_views/vis_pane_views/examples_view.js b/sproutcore/apps/fp/views/info_views/vis_pane_views/examples_view.js new file mode 100644 index 000000000..85ba0b44f --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/vis_pane_views/examples_view.js @@ -0,0 +1,132 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 9/16/13 + * Time: 2:20 PM + * To change this template use File | Settings | File Templates. + */ +Footprint.ExamplesView = SC.View.extend({ + layout: { left: 0.01, right: 0.7, top: 0.30, bottom: 0.05 }, + childViews: 'headerView imagesView noExamplesListView examplesListView'.w(), + + headerView: SC.LabelView.design({ + layout: { width: 150, height: 50 }, + classNames: ['sectionTitle'], + tagName: "h1", + value: "Examples" + }), + imagesView: SC.View.design({ + layout: { top: 0.05, bottom: 0.4 }, + childViews: 'bigImageView thumbnailsView'.w(), + bigImageView: SC.View.design({ + layout : { right: 0.20 }, + childViews: 'bigImageImageView'.w(), + displayProperties: ['content'], + + content:null, + contentBinding: SC.Binding.oneWay('Footprint.builtFormMediaController*selection.firstObject.url'), + + bigImageImageView: SC.ImageView.extend({ + layout: { bottom: 0.10 }, + classNames: ['bigImage'], + + displayProperties: ['content'], + content:null, + contentBinding: SC.Binding.oneWay('.parentView*content'), + + value: function() { + return Footprint.STATIC.fmt(this.get('content')) + }.property('content').cacheable() + + }) + }), + thumbnailsView: SC.ScrollView.design({ + layout: { left: 0.80, bottom: 0.10}, + + isVisibleBinding: SC.Binding.oneWay('Footprint.builtFormActiveController*content.media').bool(), + contentView: SC.SourceListView.extend({ + rowHeight: 45, + + contentBinding: SC.Binding.oneWay('Footprint.builtFormActiveController*content.media'), + + selectionBinding: SC.Binding.from('Footprint.builtFormMediaController.selection'), + exampleView: SC.View.extend(SC.Control, { + layout: { height: 40 }, + childViews: 'imageView'.w(), + contentValueKey: 'title', + imageView: SC.ImageView.extend({ + layout: { left: 5, height: 40, width: 40 }, + displayProperties: ['content'], + + content:null, + contentBinding: SC.Binding.oneWay('.parentView*content.url'), + + value: function() { + return Footprint.STATIC.fmt(this.get('content')) + }.property('content').cacheable() + + }) + }) + }) + }) + }), + noExamplesListView: SC.LabelView.design({ + layout: { top: 0.6, bottom: 0, width: 250, height: 20}, + value: "No examples to display", + + isVisibleBinding: SC.Binding.oneWay('Footprint.builtFormActiveController*content.examples').bool().not() + + }), + examplesListView: SC.ScrollView.design({ + layout: { top: 0.6, bottom: 0}, + + isVisibleBinding: SC.Binding.oneWay('Footprint.builtFormActiveController*content.examples').bool(), + contentView: SC.SourceListView.extend({ + classNames: 'examplesList'.w(), + rowHeight: 40, + + contentBinding: SC.Binding.oneWay('Footprint.builtFormActiveController*content.examples'), + + exampleView: SC.View.extend(SC.Control, { + layout: { height: 50 }, + childViews: 'nameView url1View url2View'.w(), + + contentValueKey: 'title', + nameView: SC.LabelView.extend({ + layout: { left: 10, width: 300, height: 15}, + + content: null, + contentBinding: SC.Binding.oneWay('.parentView*content'), + valueBinding: SC.Binding.oneWay('.parentView*content.name') + + }), + url1View: SC.LabelView.extend({ + layout: { left: 10, top: 18, width: 150, height: 15}, + + content: null, + contentBinding: SC.Binding.oneWay('.parentView*content'), + value: function() { + var url = this.getPath('parentView*content.url_aerial'); + return "Street View" + + }.property('content').cacheable(), + escapeHTML: NO + + }), + url2View: SC.LabelView.extend( { + layout: { left: 150, top: 18, width: 100, height: 15 }, + + content: null, + contentBinding: SC.Binding.oneWay('.parentView*content.url_street'), + value: function() { + var url = this.get('content'); + return "Street View" + }.property('content').cacheable(), + escapeHTML: NO + + }) + }) + }) + }) +}); + diff --git a/sproutcore/apps/fp/views/info_views/vis_pane_views/visualize_pane.js b/sproutcore/apps/fp/views/info_views/vis_pane_views/visualize_pane.js new file mode 100644 index 000000000..063ceeb29 --- /dev/null +++ b/sproutcore/apps/fp/views/info_views/vis_pane_views/visualize_pane.js @@ -0,0 +1,189 @@ + +sc_require('resources/donutChartMaker'); +sc_require('resources/builtFormVisUtils'); +sc_require('views/info_views/vis_pane_views/examples_view'); +sc_require('views/info_views/vis_pane_views/development_chars_view'); +sc_require('views/info_views/vis_pane_views/donut_charts_view'); +sc_require('views/info_views/vis_pane_views/bar_chart_view'); + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.VisualizePane = SC.PanelPane.extend(SC.ActionSupport, { + + layout: { height:630, width:1300, centerX: 0, centerY: 0 }, + + contentView: SC.View.extend({ + classNames:['footprint-visualize-palette-pane'], + childViews:'mainView listView'.w(), + + content:null, + contentBinding: SC.Binding.oneWay('Footprint.builtFormActiveController.content'), + + flatBuiltFormContent: null, + flatBuiltFormContentBinding:SC.Binding.oneWay('Footprint.flatBuiltFormActiveController.content'), + + mainView: SC.View.extend({ + + layout: { left:.20 }, + + classNames:['footprint-visualize-pane-content'], + childViews:'headerView descriptionView examplesView barChartView donutChartsView rightOfBarChartView developmentCharsView footerView'.w(), + + content: null, + contentBinding: SC.Binding.oneWay('.parentView.content'), + + flatBuiltFormContent: null, + flatBuiltFormContentBinding:SC.Binding.oneWay('.parentView.flatBuiltFormContent'), + + color: null, + colorBinding: SC.Binding.oneWay('*content.medium').transform(function(medium) { + if (medium) { + return medium.getPath('content.fill.color'); + } + }), + + headerView: SC.LabelView.extend({ + classNames: ['footprint-visualize-pane-header'], + layout: { left: 0, right: 0, top: 0, height: 0.07 }, + displayProperties: ['content', 'color'], + + valueBinding: SC.Binding.oneWay('.parentView*content.name'), + + color: null, + colorBinding: SC.Binding.oneWay('.parentView.color'), + + backgroundColor:null, + backgroundColorBinding: SC.Binding.oneWay('.parentView.color'), + classNameBindings: ['hasLightBackground'], + + hasLightBackground: function() { + if (this.get('color')) { + return isLightColor(this.get('color')); + } + }.property('color').cacheable(), + + render: function(context) { + sc_super(); + } + }), + + descriptionView: SC.View.design({ + layout: { top: 0.06, left: 0, right:.65, bottom: 0.70}, + classNames: ['descriptionBlock'], + displayProperties: ['content'], + + content: null, + contentBinding: SC.Binding.oneWay('.parentView.flatBuiltFormContent'), + + render: function(context) { + var description =this.getPath('content.description'); + context = context.begin('div').push(description).end(); + + sc_super(); + } + }), + + rightOfBarChartView: SC.View.design({ + layout: { top: 0.06, left: 0.95, bottom: 0.75}, + classNames: ['rightOfBarChart'] + // this is a hack to get the rounded edge below the header + }), + + barChartView: Footprint.BarChartView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.flatBuiltFormContent') + }), + + donutChartsView: Footprint.DonutChartsView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.flatBuiltFormContent') + }), + + examplesView: Footprint.ExamplesView, + + developmentCharsView: Footprint.DevelopmentCharsView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.flatBuiltFormContent') + }), + + footerView: SC.View.extend({ + childViews: 'cancelButtonView'.w(), + classNames: ['footprint-visualize-pane-footer'], + layout: { left: 0, right: 0, top: 0.95 }, + displayProperties: ['content', 'color'], + + color: null, + colorBinding: SC.Binding.oneWay('.parentView.color'), + + render: function(context) { + sc_super(); + var color = this.get('color'); + context.addStyle("background-color", color); + }, + + cancelButtonView: SC.ButtonView.design({ + layout: {bottom: 5, right: 20, height:24, width:80}, + title: 'Close', + action: 'doClose', + isCancel: YES + }) + }) + }), + + listView: SC.ScrollView.extend({ + layout: { right:.80}, + media: null, + // TODO this doesn't make sense + loadMedia: function () { + this.set('media', Footprint.store.find(SC.Query.local( + Footprint.Medium, { + orderBy: 'key' }))); + }.observes('Footprint.scenarioActiveController.content'), + + contentView: SC.SourceListView.extend({ + isEnabledBinding: SC.Binding.oneWay('.content').bool(), + rowHeight: 20, + actOnSelect: NO, + contentIconKey: 'medium', + contentValueKey: 'name', + + contentBinding: SC.Binding.oneWay('Footprint.builtFormCategoriesTreeController.arrangedObjects'), + selectionBinding: SC.Binding.from('Footprint.builtFormCategoriesTreeController.selection'), + + exampleView: SC.View.extend(SC.Control, SC.ContentDisplay, { + classNames: ['footprint-built-form-item'], + contentDisplayProperties: ['name'], + + render: function(context) { + // Color swab + var color = this.getPath('content.medium.content.fill.color'); + context.begin() + .addClass(this.getPath('theme.classNames')) + .addClass(['sc-view', 'footprint-medium-color']) + .setStyle({ 'background-color': color }) + .end(); + // Label + context.begin() + .addClass(this.getPath('theme.classNames')) + .addClass(['sc-view', 'sc-label-view', 'footprint-built-form-item-label-view']) + .push(this.getPath('content.name')) + .end(); + }, + update: function ($context) { + $context.find('.footprint-medium-color').css('background-color', this.getPath('content.medium.content.fill.color')); + $context.find('.footprint-built-form-item-label-view').text(this.getPath('content.name')); + } + }) + }) + }) + }) +}); + diff --git a/sproutcore/apps/fp/views/loading_pane.js b/sproutcore/apps/fp/views/loading_pane.js new file mode 100644 index 000000000..339fd461c --- /dev/null +++ b/sproutcore/apps/fp/views/loading_pane.js @@ -0,0 +1,18 @@ + +Footprint.LoadingPane = SC.MainPane.extend({ + childViews: 'loadingView progressView'.w(), + + loadingView: SC.ImageView.extend({ + classNames:'loading-image'.w(), + useCanvas:NO, + layout: {centerX: 0, centerY:0, width:500, height:500}, + value:sc_static('images/loading.png') + }), + progressView: SC.ProgressView.extend({ + layout: {centerX:.0001, centerY:0, width:.2, height:16, top:0.9}, + valueBinding:SC.Binding.oneWay('Footprint.loadingStatusController.content'), + minimum:0, + maximum:10 + }) +}); + diff --git a/sproutcore/apps/fp/views/main_pane.js b/sproutcore/apps/fp/views/main_pane.js new file mode 100644 index 000000000..442178503 --- /dev/null +++ b/sproutcore/apps/fp/views/main_pane.js @@ -0,0 +1,177 @@ +sc_require('views/sections/scenario_section_view'); +sc_require('views/sections/result_section_view'); +sc_require('views/sections/map_section_view'); +sc_require('views/sections/analytic_section_view'); +sc_require('views/sections/layer_section_view'); +sc_require('views/sections/tool_section_view'); +sc_require('views/sections/built_form_section_view'); + +Footprint.MainPane = SC.MainPane.extend({ + childViews: ['headerBarView', 'bodyView'], + + headerBarView: SC.View.extend({ + layout: { height: 35 }, + classNames: ['footprint-project-section-view', 'toolbar'], + childViews: ['projectView', 'clientView', 'logoutButtonView'], + projectView: SC.View.extend({ + layout: { width: 285 }, + + contentBinding: SC.Binding.oneWay('Footprint.projectsController.content'), + selectionBinding: 'Footprint.projectsController.selection', + + childViews: ['projectLogoView', 'projectSelectView', 'projectMenuView'], + projectLogoView: SC.ImageView.extend({ + layout: { left: 5, height: 24, width: 24, centerY: 0 }, + clientBinding: SC.Binding.oneWay('Footprint.regionActiveController.client'), + projectKeyBinding: SC.Binding.oneWay("Footprint.projectActiveController.key"), + value: function () { + var client = this.get('client'), + projectKey = this.get('projectKey'); + if (client && projectKey) { + var image_path = 'images/%@_logo.png'.fmt(projectKey); + return client.STATIC.fmt(image_path); + } + }.property('client', 'projectKey').cacheable() + }), + projectSelectView: Footprint.LabelSelectView.extend({ + layout: { left: 35, right: 40, height: 22, centerY: 0, border: 1 }, + contentBinding: SC.Binding.oneWay('.parentView.content'), + selection: null, + selectionBinding: '.parentView.selection', + selectedItemBinding: '*selection.firstObject', + itemTitleKey: 'name' + }), + projectMenuView: Footprint.EditButtonView.extend({ + layout: { right: 6, width: 26, height: 22, centerY: 0, border: 1 }, + + icon: sc_static('images/section_toolbars/pulldown.png'), + + contentBinding: SC.Binding.oneWay('.parentView.content'), + + // TODO: Hook these back up for use in the statecharts. + recordType: null, + activeRecord: null, + menuItems: [ + SC.Object.create({ title: 'Get Info', isEnabled:NO}), + SC.Object.create({ title: 'Manage Projects', isEnabled:NO}) + ] + }) + }), + clientView: SC.View.extend({ + layout: { left: 280, right: 80 }, + childViews: ['titleView', 'clientLogoView'], + titleView: SC.LabelView.create({ + layout: { right: 145, height: 24, centerY: 0 }, + classNames: "footprint-right-logo-title-view", + valueBinding: SC.Binding.oneWay("Footprint.regionActiveController.name").transform(function(name) { + if (name) + return "%@ UrbanFootprint Scenario Planning Model".fmt(name); + }) + }), + clientLogoView: SC.ImageView.extend({ + layout: { width: 145, right: 0, height: 33, centerY: 0 }, + scale: SC.BEST_FIT, + valueBinding: SC.Binding.oneWay('Footprint.regionActiveController*client.logoPath') + }) + }), + logoutButtonView: SC.ButtonView.extend({ + layout: { width: 70, right: 5, height: 22, centerY: 0, border: 1 }, + classNames: ['theme-button', 'theme-button-gray'], + title: 'Logout', + action: 'doLogout' + }) + }), + + bodyView: SC.SplitView.extend({ + layout: { top: 35 }, + layoutDirection: SC.LAYOUT_VERTICAL, + childViews: ['topView', 'bottomView'], + topView: SC.SplitView.extend(SC.SplitChild, { + size: 200, + sizeOffset:-5, + canCollapse:YES, + layoutDirection: SC.LAYOUT_HORIZONTAL, + + childViews: ['scenarioSectionView', 'resultSectionView'], + scenarioSectionView: Footprint.ScenarioSectionView.extend(SC.SplitChild, { + size: 470, + sizeOffset:-5, + canCollapse:YES + }), + resultSectionView: Footprint.ResultSectionView.extend(SC.SplitChild, { + autoResizeStyle: SC.RESIZE_AUTOMATIC, + positionOffset:5, + sizeOffset:-5, + canCollapse:YES + }) + }), + bottomView: SC.SplitView.extend(SC.SplitChild, { + autoResizeStyle: SC.RESIZE_AUTOMATIC, + positionOffset:5, + sizeOffset:-5, + canCollapse:YES, + + childViews: ['sidebarView', 'mapView', 'analyticView'], + sidebarView: SC.View.extend(SC.SplitChild, { + classNames: "footprint-sidebar-view".w(), + size: 350, + sizeOffset:-5, + canCollapse:YES, + childViews: ['sidebarViewItself', 'copyrightView'], + sidebarViewItself: SC.SplitView.extend({ + layout: { bottom: 35 }, + + childViews:['layerSectionView', 'toolsetView', 'builtFormsView'], + layoutDirection: SC.LAYOUT_VERTICAL, + + layerSectionView: Footprint.LayerSectionView.extend(SC.SplitChild, { + size: 150, + autoResizeStyle: SC.RESIZE_AUTOMATIC, + sizeOffset:-5, + canCollapse:YES + }), + toolsetView: Footprint.ToolSectionView.extend(SC.SplitChild, { + size: 130, + maximumSize: 130, + minimumSize: 130, + sizeOffset: -10, + positionOffset: 5, + canCollapse:YES + }), + builtFormsView: Footprint.BuiltFormSectionView.extend(SC.SplitChild, { + size: 100, + sizeOffset: -5, + positionOffset: 5, + canCollapse:YES + }) + }), + copyrightView: SC.View.extend({ + layout: { height: 35, bottom: 0 }, + childViews: 'copyrightImageView copyrightLabelView'.w(), + classNames: "footprint-copyright-view", + copyrightImageView: SC.ImageView.extend({ + layout: { height: 35, left: 0, width: 35 }, + value: sc_static('images/default_logos/uf_thumbnail_35.png') + }), + copyrightLabelView: SC.LabelView.create({ + classNames: "footprint-copyright-label-view", + value: 'UrbanFootprint rev. 2014.1.27 \n © 2013 Calthorpe Associates', + layout: {top: 0.05, left: 40} + }) + }) + }), + mapView: Footprint.MapSectionView.extend(SC.SplitChild, { + autoResizeStyle: SC.RESIZE_AUTOMATIC, + sizeOffset:-5, + positionOffset:5, + canCollapse:NO + }), + analyticView: Footprint.AnalyticSectionView.extend(SC.SplitChild, { + size: 275, + sizeOffset:-5, + positionOffset:5, + canCollapse:YES + }) + }) + }) +}); diff --git a/sproutcore/apps/fp/views/presentation/map/map_controls.js b/sproutcore/apps/fp/views/presentation/map/map_controls.js new file mode 100644 index 000000000..cbbd91b80 --- /dev/null +++ b/sproutcore/apps/fp/views/presentation/map/map_controls.js @@ -0,0 +1,82 @@ + +sc_require('resources/polymaps'); + +/*** + * Mixin to MapView to apply view controls + */ +Footprint.MapControls = { + + /*** + * Adds a switcher to the map to switch between parcels + */ + addSwitcher: function() { + d3.select("#map").insert("div",":first-child").attr("id","layerswitcher"); + po.switcher(map, layers, {title: 'parcel layers'}).container(document.getElementById("layerswitcher")); + }, + + removeBrush: function() { + d3.selectAll(".brush").remove(); + }, + + infoBoxCreate: function() { + d3.select("#map") + .insert("div", ":first-child") + .attr("id", "identify-popup") + .style("margin", d3.select("#map").node().clientWidth/2 - 100) + .append("p") + .text("ESC") + .on('click', infoBoxRemove); + }, + + infoBoxRemove: function() { + d3.selectAll("#identify-popup") + .remove(); + }, + + addToolSelector: function(text, onclick, x) { + d3.select("#map") + .insert("div", ":first-child") + .classed("tool-selector", true) + .on("click", onclick) + .style("margin-left", x) + .append("p") + .text(text) + }, + + makePallete: function() { + d3.select("#map") + .insert("div", ":first-child") + .classed('pallete', true); + for (var bf in builtForms) { + d3.select('.pallete') + .append('div') + .classed('builtformbutton', true) + .attr('id', bf) + .style('background-color', builtForms[bf].color) + .style('margin-left', function() {return w - 100}) + .on('click', updateBuiltFormWithSelection) + .append('p') + .text(bf) + } + }, + + dblClickFeature: function(f, evt, fields) { + infoBoxRemove(); + d3.select(f.element).classed('selected', true); + infoBoxCreate(); + + var d = f.data.properties; + for (var i = 0; i < fields.length; i++) { + popupText(fields[i], d[fields[i]])} + }, + + // steps through the items that a user may want to remove, in a sensible order, and removes them one at a time + clearAll: function() { + var identifyBox = d3.selectAll('#identify-popup'); + var brushes = d3.selectAll('.brush'); + if (identifyBox[0].length > 0) {identifyBox.remove() } + else if (brushes[0].length > 0) { brushes.remove() } + else {unselect();} + } + +}; diff --git a/sproutcore/apps/fp/views/presentation/map/map_layer_group.js b/sproutcore/apps/fp/views/presentation/map/map_layer_group.js new file mode 100644 index 000000000..59bbf0612 --- /dev/null +++ b/sproutcore/apps/fp/views/presentation/map/map_layer_group.js @@ -0,0 +1,121 @@ + +/*** + * A configuration of the polymaps layer for a particular Footprint.Layer instance + * @type {*} + */ +Footprint.MapLayerGroup = SC.Object.extend({ + + /*** + * The attribute names of the three layers in each mapLayerGroup. + * The vector and selectionLayer are both vector layers, the raster is ... a raster + * raster and vector are different representations of the underlying geometries, whereas selectionLayer is a subset + * of the geometries of raster/vector that the user has selected. + */ + mapLayerGroupLayers:['raster', 'vector', 'selectionLayer'], + + map:null, + + /*** + * Adds all the mapLayerGroup layers to the map + */ + addLayersToMap: function() { + var map = this.get('map'); + this.get('mapLayerGroupLayers').forEach(function(name) { + var mapLayer = this.get(name); + if (mapLayer) + map.add(mapLayer); + }, this); + }, + + // An example of adding events to vector features + _addListersToFeatures: function(mapLayer) { + /* + if (name == 'selectionLayer' && mapLayer.features) { + var features = mapLayer.features(); + features.forEach(function(feature) { + // add double-click listener, which pops up with information specified in the fields + feature.element.addEventListener("dblclick", (function(feature) { + return function(event) { + SC.AlertPane.info(feature); + } + })(feature), false); + + // add click listener, which checks if the option key is being pressed before acting + feature.element.addEventListener("click", (function(feature) { + return function(event) { + if (event.altKey) { + SC.AlertPane.info(feature); + } + } + })(feature), false); + }, this); + } + */ + }, + + /*** + * Bound to layer.visible to indicate whether the group should be shown or not. + */ + visible:null, + visibleBinding:SC.Binding.oneWay('*layer.applicationVisible'), + + onVisible: function() { + if (!this.didChangeFor('onVisibleChange', 'visible')) + return; + this.setVisibilityBasedOnZoom(); + }.observes('.visible'), + + /*** + * Determines if the given mapGroupLayer is visible at the current map zoom level + * @param mapLayerGroupLayerName + * @returns {*} + */ + visibleAtZoomLevel: function(mapLayerGroupLayerName) { + var map = this.get('map'); + if (mapLayerGroupLayerName=='raster') + return map.zoom() < 19; + if (mapLayerGroupLayerName=='vector') + return map.zoom() >= 19; + return YES; + }, + + /*** + * Sets visibility of each mapLayerGroupLayer based on overall visibility and zoom level + */ + setVisibilityBasedOnZoom: function() { + this.get('mapLayerGroupLayers').forEach(function(name) { + var mapLayer = this.get(name); + if (mapLayer) + mapLayer.visible(this.getPath('layer.applicationVisible') && this.visibleAtZoomLevel(name)); + }, this); + }, + /*** + * Set visibility based on the property of the layer + */ + setVisibility: function() { + this.get('mapLayerGroupLayers').forEach(function(name) { + var mapLayer = this.get(name); + if (mapLayer) + mapLayer.visible(this.get('visible')) + }, this); + }, + + + /*** + * The Footprint.Layer instance + */ + layer: null, + /*** + * The vector map layer + */ + vector: null, + /*** + * The raster map layer + */ + raster: null, + /*** + * The user selection layer + */ + selectionLayer: null, + selection:null // not sure if we need this now +}); diff --git a/sproutcore/apps/fp/views/presentation/map/map_painting.js b/sproutcore/apps/fp/views/presentation/map/map_painting.js new file mode 100644 index 000000000..5a9ab69b6 --- /dev/null +++ b/sproutcore/apps/fp/views/presentation/map/map_painting.js @@ -0,0 +1,85 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 2/19/13 + * Time: 12:28 PM + * To change this template use File | Settings | File Templates. + */ +sc_require('views/presentation/map/polybrush'); + +/*** + * Mixin to map view to apply painting functions + */ +Footprint.MapPainting = { + + /*** + * Activate the given brush by removing the previous brush from the svg and adding the given one + * @param paintToolName the name of the brush in the tools object + */ + activatePaintTool: function() { + if (!Footprint.toolController.get('selectorIsEnabled')) { + logWarning("Attempt to activate tool when selectorIsEnabled is false. This should not be possible"); + return; + } + // Set or reset the svg of the selection tool + this.resetCurrentSelectionTool(); + // Pop the compass back to the top of the stack. + var map = Footprint.mapController.get('content'), + compass = po.compass(map); + if (!map) throw new Error("HOW DID I GET HERE WITH NO MAP OH GOD"); + map.remove(compass); + map.add(compass); + }.observes('Footprint.mapToolsController.activePaintTool', 'Footprint.toolController.selectorIsEnabled'), + + activateNavigationTool: function() { + // If selection is not enabled or no selection tool is selected + if (!Footprint.toolController.get('selectorIsEnabled') || !Footprint.mapToolsController.get('activePaintTool')) { + // Get the dom element of the map + var mapNode = d3.select(this.get('mapNode')); + // Remove previously created brushes + mapNode.selectAll(".paint-tool").remove(); + } + }.observes('Footprint.mapToolsController.activePaintTool', 'Footprint.toolController.selectorIsEnabled'), + + resetCurrentSelectionToolObserver: function() { + if (Footprint.mapToolsController.set('selectionToolNeedsReset', YES)) { + Footprint.mapToolsController.set('selectionToolNeedsReset', NO); + this.resetCurrentSelectionTool(); + } + }.observes('Footprint.toolController.selectionToolNeedsReset'), + + resetCurrentSelectionTool: function() { + // The d3.svg path creator + var paintTool = Footprint.mapToolsController.get('activePaintTool'); + if (!paintTool) + return; + var brush = paintTool.get('brush'); + // Get the dom element of the map + var mapNode = d3.select(this.get('mapNode')); + // Remove previously created brushes + mapNode.selectAll(".paint-tool").remove(); + // Create the brush by inserting a g element into the map-interface svg + mapNode.select("#map-interface") + .insert("svg:g") + .classed('paint-tool', true) + // Call the brush to initiate selection + .call(brush); + }, + + repaint: function(e) { + var layerGroup = this.get('activeLayerGroup'); + if (!layerGroup) { + return false; + } + // TODO document what this does + if (layerGroup.get('selection').length > 0) { + for (var i = 0; i < e.features.length; i++) { + var f = d3.select(e.features[i].element); + f.classed("selected", function() { + var id = parseInt(this.id.slice(2)); + return !$.inArray(id, layerGroup.selection) == -1; + }) + } + } + } +} diff --git a/sproutcore/apps/fp/views/presentation/map/map_styling.js b/sproutcore/apps/fp/views/presentation/map/map_styling.js new file mode 100644 index 000000000..fc1d81064 --- /dev/null +++ b/sproutcore/apps/fp/views/presentation/map/map_styling.js @@ -0,0 +1,60 @@ +/** + * Created with PyCharm. + * User: calthorpe + * Date: 2/19/13 + * Time: 11:03 AM + * To change this template use File | Settings | File Templates. + */ + +sc_require('resources/polymaps'); +sc_require('resources/d3.v3.js'); + +/*** + * Mixin for MapView to apply styling to layers + * + */ +Footprint.MapStyling = { + + stylingLookup:null, + + /*** + * Initializes the styling elements used by the map. + */ + initStyling: function() { + var color = d3.scale.linear().domain([1, 15]).range(["red", "green"]); + + this.stylingLookup = SC.Object.create({ + quantize_income: d3.scale.quantize().domain([1,140000]) + .range([1,2,3,4,5,6,7,8,9,10,12,13,14,15]), + + base_stylist: po.stylist() + .attr('id', function(d) { return "id" + d.properties.geography_id; }) + .attr('class', 'parcel_geometry') + .title(function(d){ return d.properties.geography_id; }) + .style('fill', function(d) { + if (d.properties.hh_avg_inc==0) { return "white"; } + else { return color(this.quantize_income(d.properties.hh_avg_inc)); } + }), + + built_form_stylist: po.stylist() + .attr('id', function(d){ + return "id" + d.properties.geography_id; + }) + .attr('class', function(d) { + return "parcel_geometry built_form_id-" + d.properties.built_form_id; + }) + .title(function(d){ + return d.properties.geography_id; + }), + + selection_stylist: po.stylist() + .attr('stroke', 'Yellow').attr('fill','Red;').attr('class', 'selected') + }) + }, + + built_form_css: function(layer) { + // takes a built_form_feature layer and generates css to style the svg elements + var built_form_style = layer.medium_context.attributes.built_form_id.equals; + for (key in built_form_style) {$("built_form_id-" + key).css("fill", built_form_style[key].fill.color)} + } +}; diff --git a/sproutcore/apps/fp/views/presentation/map/map_tools.js b/sproutcore/apps/fp/views/presentation/map/map_tools.js new file mode 100644 index 000000000..5ca2056d1 --- /dev/null +++ b/sproutcore/apps/fp/views/presentation/map/map_tools.js @@ -0,0 +1,255 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +Footprint.MapTools = SC.Object.extend({ + + /*** + * A reference to the mapView that these tools interact with + */ + mapView:null, + /*** + * Point brush + */ + pointbrush:null, + /*** + * A d3 polygon selection bush + */ + polybrush:null, + /** + * A d3 rectangle selection brush + */ + rectanglebrush:null, + + init: function() { + this.set('polybrush', this.initPolygonBrush()); + this.set('rectanglebrush', this.initRectangleBrush()); + this.set('pointbrush', this.initPointBrush()); + }, + + initPolygonBrush: function() { + var self = this; + return Footprint.BrushTool.create({ + mapView: this.get('mapView'), + brush:this._initBrush(d3.svg.polybrush), + selector:'.polybrush', + /*** + * Returns the closed polygon geometry based on the current points selected by the tool + * @returns {*} + */ + geometry:function() { + var coordinates = []; + var polybrush = d3.select(".polybrush"); + var polybrushNodes = polybrush.data()[0]; + if (!polybrushNodes) + return; + + for (var i=0; i < polybrushNodes.length; i++ ) { + var coord = new jsts.geom.Coordinate(polybrushNodes[i][0], polybrushNodes[i][1]); + coordinates.push(coord); + } + + // adds the first node as the last node, closing the polygon shell + coordinates.push(new jsts.geom.Coordinate(polybrushNodes[0][0], polybrushNodes[0][1])); + return self.polygonFromLinearRing(coordinates); + }, + + toString: function() { + return 'Footprint.PolygonBrushTool'; + } + }); + }, + + initRectangleBrush: function() { + var self = this; + return Footprint.BrushTool.create({ + mapView: this.get('mapView'), + brush:this._initBrush(d3.svg.brush), + selector:null, // not needed currently + /*** + * Creates the polygon geometry from the rectangle + * @returns {*} + */ + geometry: function() { + var extent = this.get('brush').extent(); + var bbox0 = ([extent[0][0], extent[0][1]]); + var bbox1 = ([extent[1][0], extent[1][1]]); + var c1 = new jsts.geom.Coordinate(bbox0[0], bbox0[1]); + var c2 = new jsts.geom.Coordinate(bbox1[0], bbox0[1]); + var c3 = new jsts.geom.Coordinate(bbox1[0], bbox1[1]); + var c4 = new jsts.geom.Coordinate(bbox0[0], bbox1[1]); + return self.polygonFromLinearRing([c1,c2,c3,c4,c1]); + }, + + toString: function() { + return 'Footprint.RectangleBrushTool'; + } + }); + }, + initPointBrush: function() { + var self = this; + return Footprint.BrushTool.create({ + mapView: this.get('mapView'), + brush:this._initBrush(d3.svg.brush), + selector:null, // not needed currently + /*** + * Creates the polygon geometry from the rectangle + * @returns {*} + */ + geometry: function() { + var extent = this.get('brush').extent(); + var coordinate = new jsts.geom.Coordinate(extent[0][0], extent[0][1]); + return self.projectedPointFromCoordinate(coordinate) + }, + + toString: function() { + return 'Footprint.PointBrushTool'; + }, + startSelection: function() { + Footprint.statechart.sendAction('doStartSelection'); + this.get('brush').dragStop(); + } + }); + }, + + /*** + * Converts any number of point coordinates to a polygon. The end coordinates must already be closed + * @param coordinates + * @returns {*} + */ + polygonFromLinearRing: function(coordinates) { + var shell = geometryFactory.createLinearRing(coordinates); + return this.projectPolygon(geometryFactory.createPolygon(shell)); + }, + + /*** + * Translates the polygon from screen coordinates to map coordinates and returns geojson of the coordinates + * @param polygon + * @returns {{type: string, coordinates: Array}} + */ + projectPolygon: function(polygon) { + var coordinates = polygon.getCoordinates(); + var map = this.getPath('mapView.map'); + + return { + type: "MultiPolygon", + coordinates: [[coordinates.map(function(coordinate) { + var projectedCoordinates = map.pointLocation(coordinate); + return [projectedCoordinates.lon, projectedCoordinates.lat]; + }, this) ]] + }; + }, + + /*** + * Extract + * @param multiPolygon + * @returns {{type: string, coordinates: Array}} + */ + extentsFromPolygons: function(multiPolygon) { + // TODO + var coordinates = multiPolygon.map(function(polygon) { + polygon.getCoordinates(); + }); + }, + + /*** + * Translates the x,y coordinate to the map projection + * @param coordinate + * @returns {{type: string, coordinates: Array}} + */ + projectedPointFromCoordinate: function(coordinate) { + + var map = this.getPath('mapView.map'); + var projectedCoordinate = map.pointLocation(coordinate); + + return { + type: "Point", + "coordinates": [ + projectedCoordinate.lon, projectedCoordinate.lat + ] + } + }, + + _initBrush: function(brushMaker) { + var w = window.innerWidth; + var h = window.innerHeight; + var self = this; + return brushMaker() + .x(d3.scale.identity().domain([0,w])) + .y(d3.scale.identity().domain([0,h])); + } +}); + + /*** + * Represents a tool used for painting/selection + * @type {*} + */ +Footprint.PaintTool = SC.Object.extend({ + + mapView: null, + /*** + * The d3.svg tool used for making selections and/or painting + */ + brush:null, + /*** + * The selector string to access the svg element holding the tool's path data() + */ + selector:null, + /*** + * Returns the path drawn by tool by accessing the data() of the element indicated by the selector + */ + geometry:function() { + + } +}); + + /*** + * Extends PaintTool to add actions based on the d3.svg.brush interface + * @type {*} + */ +Footprint.BrushTool = Footprint.PaintTool.extend({ + /*** + * Overload init to set up the brush events + * Brush start clears the activeLayerSelection + */ + init:function() { + sc_super(); + var self = this; + this.get('brush') + .on('brushstart', function() { + // Clear the activeLayerSelection's geometry when we start. + // TODO this could also be additive or a continuation of the previous polygon + SC.run(self.startSelection, self); + }) + .on('brush', function() { + SC.run(self.addToSelection, self); + }) + .on('brushend', function() { + SC.run(self.endSelection, self); + }) + Footprint.statechart.sendAction('doCancelLayerSelectionUpdate'); + }, + + startSelection: function() { + Footprint.statechart.sendAction('doStartSelection'); + }, + + addToSelection: function() { + Footprint.statechart.sendAction('doAddToSelection'); + }, + + endSelection: function() { + Footprint.statechart.sendAction('doEndSelection'); + } +}); diff --git a/sproutcore/apps/fp/views/presentation/map/polybrush.js b/sproutcore/apps/fp/views/presentation/map/polybrush.js new file mode 100644 index 000000000..011c293a1 --- /dev/null +++ b/sproutcore/apps/fp/views/presentation/map/polybrush.js @@ -0,0 +1,218 @@ +/* + Copyright (c) 2012 Geoffrey T. Bell + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +(function(d3) { + d3.svg.polybrush = function() { + var dispatch = d3.dispatch("brushstart", "brush", "brushend"), + x = null, + y = null, + extent = [], + firstClick = true, + firstTime = true, + wasDragged = false, + origin = null, + line = d3.svg.line() + .x(function(d) { + return d[0]; + }) + .y(function(d) { + return d[1]; + }); + var brush = function(g) { + g.each(function() { + var bg, e, fg; + g = d3.select(this); + bg = g.selectAll(".background").data([0]); + fg = g.selectAll(".extent").data([extent]); + g.style("pointer-events", "all").on("click.brush", addAnchor); + bg.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); + fg.enter().append("path").attr("class", "extent").classed("polybrush", true).style("cursor", "move"); + if (x) { + e = scaleExtent(x.range()); + bg.attr("x", e[0]).attr("width", e[1] - e[0]); + } + if (y) { + e = scaleExtent(y.range()); + bg.attr("y", e[0]).attr("height", e[1] - e[0]); + } + }); + }; + var drawPath = function() { + return d3.selectAll(".polybrush").attr("d", function(d) { + return line(d) + "Z"; + }); + }; + var scaleExtent = function(domain) { + var start, stop; + start = domain[0]; + stop = domain[domain.length - 1]; + if (start < stop) { + return [start, stop]; + } else { + return [stop, start]; + } + }; + var withinBounds = function(point) { + var rangeX, rangeY, _x, _y; + rangeX = scaleExtent(x.range()); + rangeY = scaleExtent(y.range()); + _x = Math.max(rangeX[0], Math.min(rangeX[1], point[0])); + _y = Math.max(rangeY[0], Math.min(rangeY[1], point[1])); + return point[0] === _x && point[1] === _y; + }; + var moveAnchor = function(target) { + var moved, point; + point = d3.mouse(target); + if (firstTime) { + extent.push(point); + firstTime = false; + } else { + if (withinBounds(point)) { + extent.splice(extent.length - 1, 1, point); + } + drawPath(); + dispatch.brush(); + } + }; + var closePath = function() { + var w; + w = d3.select(window); + w.on("dblclick.brush", null).on("mousemove.brush", null); + firstClick = true; + if (extent.length === 2 && extent[0][0] === extent[1][0] && extent[0][1] === extent[1][1]) { + extent.splice(0, extent.length); + } + d3.select(".extent").on("mousedown.brush", moveExtent); + return dispatch.brushend(); + }; + var addAnchor = function() { + var g, w, + _this = this; + g = d3.select(this); + w = d3.select(window); + firstTime = true; + if (wasDragged) { + wasDragged = false; + return; + } + if (firstClick) { + extent.splice(0, extent.length); + firstClick = false; + d3.select(".extent").on("mousedown.brush", null); + w.on("mousemove.brush", function() { + return moveAnchor(_this); + }).on("dblclick.brush", closePath); + dispatch.brushstart(); + } + if (extent.length > 1) { + extent.pop(); + } + extent.push(d3.mouse(this)); + return drawPath(); + }; + var dragExtent = function(target) { + var checkBounds, fail, p, point, scaleX, scaleY, updateExtentPoint, _i, _j, _len, _len1; + point = d3.mouse(target); + scaleX = point[0] - origin[0]; + scaleY = point[1] - origin[1]; + fail = false; + origin = point; + updateExtentPoint = function(p) { + p[0] += scaleX; + p[1] += scaleY; + }; + for (_i = 0, _len = extent.length; _i < _len; _i++) { + p = extent[_i]; + updateExtentPoint(p); + } + checkBounds = function(p) { + if (!withinBounds(p)) { + fail = true; + } + return fail; + }; + for (_j = 0, _len1 = extent.length; _j < _len1; _j++) { + p = extent[_j]; + checkBounds(p); + } + if (fail) { + return; + } + drawPath(); + return dispatch.brush({ + mode: "move" + }); + }; + var dragStop = function() { + var w; + w = d3.select(window); + w.on("mousemove.brush", null).on("mouseup.brush", null); + wasDragged = true; + return dispatch.brushend(); + }; + var moveExtent = function() { + var _this = this; + d3.event.stopPropagation(); + d3.event.preventDefault(); + if (firstClick && !brush.empty()) { + d3.select(window).on("mousemove.brush", function() { + return dragExtent(_this); + }).on("mouseup.brush", dragStop); + origin = d3.mouse(this); + } + }; + brush.isWithinExtent = function(x, y) { + var i, j, len, p1, p2, ret, _i, _len; + len = extent.length; + j = len - 1; + ret = false; + for (i = _i = 0, _len = extent.length; _i < _len; i = ++_i) { + p1 = extent[i]; + p2 = extent[j]; + if ((p1[1] > y) !== (p2[1] > y) && x < (p2[0] - p1[0]) * (y - p1[1]) / (p2[1] - p1[1]) + p1[0]) { + ret = !ret; + } + j = i; + } + return ret; + }; + brush.x = function(z) { + if (!arguments.length) { + return x; + } + x = z; + return brush; + }; + brush.y = function(z) { + if (!arguments.length) { + return y; + } + y = z; + return brush; + }; + brush.extent = function(z) { + if (!arguments.length) { + return extent; + } + extent = z; + return brush; + }; + brush.clear = function() { + extent.splice(0, extent.length); + return brush; + }; + brush.empty = function() { + return extent.length === 0; + }; + + d3.rebind(brush, dispatch, "on"); + + return brush; + }; +})(d3); \ No newline at end of file diff --git a/sproutcore/apps/fp/views/presentation/result/chart_legend_view.js b/sproutcore/apps/fp/views/presentation/result/chart_legend_view.js new file mode 100644 index 000000000..20f01a613 --- /dev/null +++ b/sproutcore/apps/fp/views/presentation/result/chart_legend_view.js @@ -0,0 +1,90 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + + +Footprint.ChartLegendView = SC.View.extend({ + + layout: { left:0.78, top:0.05, bottom: 0.03 }, + classNames: ['result-legend-view'], + + keys: null, + columnToLabel: null, + + didCreateLayer: function () { + this.notifyPropertyChange('data'); + }, + + displayProperties:['data','keys','columnToLabel'], + /*** + * Create a legend for the graph + */ + update: function (context) { + if (!this.get('keys') || !this.get('columnToLabel')) + return; + + // sets legend margins + var legendMargins = {left: 2, top: 2, right: 2, bottom: 2}, + legendWidth = 300, // - (legendMargins.left + legendMargins.right), + legendSquare = 10, + legendRowHeight = 15; + + // draws legend box + var legendSvg = d3.selectAll(context).append("svg") + .attr("width", legendWidth) + .attr("height", legendRowHeight) + .append("g"); + + var numKeys = this.get('keys').length; + + var legend = legendSvg.selectAll("legend") + .data(this.get('keys').map(function(key) { + return this.get('columnToLabel')[key]; + }, this).slice()) + .enter().append("g") + .attr("class", "legend"); + +// .attr("transform", function(d,i) { +// +// // Shift every other legend item over so that there are two columns of legend items +// var extraHorizontalShift = (i%2 == 0) ? legendWidth/2 : 0; +// // Shift down each row of two legend items +// var verticalShift = Math.floor(i/2)*legendRowHeight; +// +// return "translate(" + (legendMargins.left + extraHorizontalShift) + ", " + verticalShift + ")"; +// }); + + // draws swatches + var colorScale = this.getPath('parentView.colorScale'); + legend.append("rect") + .data(this.getPath('parentView.data')) + .attr("x", 2) + .attr("y", function(d, i){ return i * 12;}) + .attr("width", legendSquare) + .attr("height", legendSquare) + .style("fill", function(d, i) { return colorScale(i); }) + .style("stroke","#d0dae3") + .style("stroke-width", "1"); + + // writes key names + legend.append("text") + .attr("x", legendSquare + 5) + .attr("y", function(d, i){ return i * 12 + 9;}) + .style("text-anchor", "start") + .text(function(d) { return d; }); + + var isStackable = this.getPath('content.configuration.stackable'); + + } +}); diff --git a/sproutcore/apps/fp/views/presentation/result/chart_view.js b/sproutcore/apps/fp/views/presentation/result/chart_view.js new file mode 100644 index 000000000..070d059a2 --- /dev/null +++ b/sproutcore/apps/fp/views/presentation/result/chart_view.js @@ -0,0 +1,712 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/presentation/result/chart_legend_view'); + +Footprint.ChartView = SC.View.extend({ + + classNames: 'chart-view'.w(), + childViews: 'legendView graphView'.w(), + resultLibraryKey: null, + layout: {top: 25}, + + content:null, + /*** + * The scenarios which form the basis of each sample + */ + scenarios:null, + + /*** + * Returns a dict that maps result query column names to label names. + * (e.g. sum__du: Dwelling Unit Sum) + * @returns {*} + * @private + */ + columnToLabel: function() { + return this.getPath('content.configuration.column_to_label'); + }.property('content').cacheable(), + + /*** + * Returns the keys representing the result query columns (a.k.a the aggregate column name) + * @returns {*} + * @private + */ + keys: function() { + var attributes = this.getPath('content.configuration.attributes'); + var attributeToColumn = this.getPath('content.configuration.attribute_to_column'); + return SC.none(attributeToColumn) ? + null : + attributes.map(function (attribute) { return attributeToColumn[attribute]}) + + }.property('content').cacheable(), + + /*** + * The content of this View is a Result. This property returns that Result for every Scenario of scenarios + * The results include one matching content + */ + results: function() { + if (!this.get('scenarios')) + return; + return this.get('scenarios').map(function(scenario, i) { + + // Match the ResultPage with the resultLibraryActiveController key. + var resultPage = scenario.getPath('presentations.results').filter(function(resultPage) { + return this.get('resultLibraryKey') == resultPage.get('key'); + }, this)[0]; + + // Find the Result of the Scenario matching the key of the content Result + return resultPage ? resultPage.get('results').filter(function(result) { + return result.getPath('db_entity_key') == this.getPath('content.db_entity_key'); + }, this)[0] || null : null; + }, this).compact(); + }.property('scenarios', 'scenarios.[]').cacheable(), + + /*** + * The names of the samples used to label the groups or stacked samples + */ + sampleNames: function() { + + if (!this.get('scenarios')) + return []; + // TODO don't use the same code here as results + var scenarios = this.get('scenarios').filter(function(scenario, i) { + // Match the ResultPage with the resultLibraryActiveController key. + var resultPage = scenario.getPath('presentations.results').filter(function(resultPage) { + return this.get('resultLibraryKey') == resultPage.get('key'); + }, this)[0]; + + // Find the Result of the Scenario matching the key of the content Result + return resultPage ? resultPage.get('results').filter(function(result) { + return result.getPath('db_entity_key') == this.getPath('content.db_entity_key'); + }, this)[0] || null : null; + }, this).compact(); + + return scenarios.mapProperty('name'); + }.property('scenarios', 'scenarios.[]').cacheable(), + + /*** + * Create the data structure for the charts. This needs to be recreated when query results change + * TODO Eventually we should do transitions when the query data changes. We do this by uniquely identifying + * each datum so that d3 knows that it is changing rather than being replaced + * @returns {*|Array|Array} The outer array is each series (e.g. du__sum, pop__sum) + * Within each series is the datum for each sample (Scenario) of the series, + * which is the d3 plot datum containing: + * Of these all are required except perhaps the value key, which we might remove. + * [ + * [(scenarioA/seriesA, scenarioB/seriesA, scenarioC/seriesA, ...)], + * [(scenarioA/seriesB, scenarioB/seriesB, scenarioC/seriesB)], + * ... + * ] + * where each (../..) is a datum: + * {id: series index, value:scenario/series value, key:series name, x:xpos,y:value} + * @private + */ + data: function() { + var keys = this.get('keys'); + if (!keys || this.getPath('results.length') == 0) + return null; + // Build the matrix by iterating over scenario Result then iterating over the series of query aggregates. Then Transpose the matrix so that are outer arrays are each series and within each array are each sample of that series. + var raw_data = d3.transpose(this.get('results').map(function(resultRecord, i) { + + // simplify the column names and create a lookup dict to the datum + // {du__sum:5, emp__sum:6} => {du:5, emp:6} + var result_lookup = $.mapObjectToObject(resultRecord.get('query'), + function(key, value) { + // Remove the aggregate part of the column name (e.g. '__sum') + // Zero out null values so d3 can process + return [key.substr(0, key.indexOf('__',0)), parseFloat(value || 0)]; + }); + + // Create the d3 series of data for the Result. Give each datum a unique id + // by index in the series. I'm not sure if this can't just be i + var data = keys.map(function(key, i) { + return { id: i, key: key, label: this.get('columnToLabel')[key], value: result_lookup[key] }; + }, this); + + var sum = d3.sum(data, function(d) {return d.value }); + + data = data.map(function (datum) {datum.percent = datum.value/sum; return datum; }); + + // Add an x and y to each datum. x is based on a spacing function and y is simply the datum value + return data.map(function(datum) { + return this._convertToPlotData(datum, i, i*10+i); + }, this); + + }, this)); + return d3.layout.stack()(raw_data); + }.property('results').cacheable(), + + + /*** + * Given a sample/series specific datum, this adds an x and a y to the object. + * The x is determined by the given parameter and determines spacing. The y is the datum value + * which determines the bar height + * @param datum + * @param index + * @param x: TODO we might be able to default this to something sensible + * @returns {*} + * @private + */ + _convertToPlotData: function (datum, index, x){ + return $.extend({x:x, y:datum.value}, datum); + }, + + /*** + * Creates the color scale based on the Medium content colorRange, + * which is simply an array of two or more colors + */ + colorScale: function() { + return d3.scale.linear() + // The domain is 0 to number of samples + .domain([0, this.get('data').length - 1]) + // Map the domain to the inclusive range of values between the colors + .range(this.getPath('content.medium.content.colorRange')); + }.property('mediumContent').cacheable(), + + legendView: Footprint.ChartLegendView.extend({ + keysBinding: SC.Binding.oneWay('.parentView.keys'), + columnToLabelBinding: SC.Binding.oneWay('.parentView.columnToLabel') + }), + + /*** + * The view containing the chart. The chart is created by using d3 to draw an SVG element + * in the view's div + */ + graphView: SC.View.extend({ + + displayProperties: ['content', 'isStacked'], + + // Leave some height for the title, radio buttons, and legend + layout: { left:0, bottom: 0.03, width:0.78, top: 10}, + + /*** + * Indicates whether or not the chart has been created + */ + initialized:NO, + + // This is our template Result. We use it to find the same keyed Result in all the Scenarios + // in scenarios + content:null, + contentBinding:SC.Binding.oneWay('.parentView.content'), + + /*** + * A reference to all scenarios of the parent Project to enable side-by-side comparison + */ + scenarios:null, + scenariosBinding:SC.Binding.oneWay('.parentView.scenarios'), + + sampleNames:null, + sampleNamesBinding:SC.Binding.oneWay('.parentView.sampleNames'), + + /** + * The number of samples + */ + sampleCount: 1, + + seriesCount: function() { + // We map here just to count the key/values + return $.map(this.getPath('content.query'), function(v,k) {return k}).length; + }.property('content').cacheable(), + + /*** + * The result.medium.content, whose colors can be updated by the user to change the chart colors + * If the Result is saved back to the server the color choices will be preserved + * Note that only the content Result color is used. The stored color of the Result of the + * other Scenarios are ignored + */ + mediumContent: null, + mediumContentBinding:SC.Binding.oneWay('.content.medium.content'), + + /*** + * Mirror the parent isStacked + */ + isStacked: null, + isStackedBinding: SC.Binding.oneWay('.parentView.isStacked'), + + /*** + * Calculates the max y for the grouped version of the chart by iterating over every datum's y + * Calculates the max y for the stacked version of the chart by iterating over each sample + * and aggregating the series' ys for each to find the greatest stacked height. + */ + + ySum: function() { + self = this; + return d3.sum(self.getPath('parentView.data'), function(sample) { + return d3.sum(sample, function(d) { + return self.get('isStacked') ? d.y0 + d.y : d.y; + }); + }); + }.property('data', 'isStacked').cacheable(), + + + yMax: function() { + self = this; + return d3.max(self.getPath('parentView.data'), function(sample) { + return self.get('isStacked') ? + d3.sum(sample, function(d) { + return d.y0 + d.y + }) : + d3.max(sample, function(d) { + return d.y + }); + }); + }.property('data', 'isStacked').cacheable(), + + yMin: function() { + self = this; + return d3.min(self.getPath('parentView.data'), function(sample) { + return d3.min(sample, function(d) { + return self.get('isStacked') ? d.y0 + d.y : d.y; + }); + }); + }.property('data', 'isStacked').cacheable(), + + /** + * Sets the scale based on yMax + */ + yScale: function() { + self = this; + return d3.scale.linear() + // This range should extend from either 0 or the minimum y value (if negative) + // to the maximum y value + .domain([d3.min([this.get('yMin'), 0]), d3.max([0, this.get('yMax')])]) + .range([this.get('height'), 15]); + }.property('yMax', 'height'), + + /** + * Sets the scale based on the sampleCount and width + */ + xScale: function() { + return d3.scale.ordinal() + .domain(d3.range(this.get('sampleCount'))) + // Sets bands for labels and ratio of groups to space between + .rangeRoundBands([0, this.get('width')], 20); + }.property('sampleCount', 'width'), + + /*** + * + * Margin for the axes to fit within. The graph g element is translated + * and scaled according to these + */ + margin: { left:0.05, top:0.0}, + + /*** + * The calculated graph width, which subtracts the margins + * The SVG will still use the full width + */ + width: function() { + return 284 - this.get('leftMarginWidth'); + }.property('leftMarginWidth').cacheable(), + + leftMarginWidth:function() { + return 284 * this.get('margin').left; + }.property().cacheable(), + + /*** + * The calculated chart height, which subtracts the margins + * The SVG will still use the full height + */ + height:function() { + return 120 - this.get('topMarginHeight'); + }.property('topMarginHeight').cacheable(), + /*** + * The calculated height of the bottom margin + */ + // bottomMarginHeight:function() { + // return 145 * this.get('margin').bottom; + // }.property().cacheable(), + + topMarginHeight:function() { + return 120 * this.get('margin').top; + }.property().cacheable(), + + /*** + * All of the series elements directly under the graph element. + */ + seriesSet:null, + + /*** + * Stores the scaler that determines the x positioning of the bars based on whether they are stacked or not + */ + seriesSpacer:null, + /*** + * Stores the d3 select set of all bars in the bar graph + */ + barSet:null, + + didCreateLayer:function() { + this.notifyPropertyChange('data'); + }, + + data: null, + dataBinding: SC.Binding.oneWay('.parentView.data'), + displayProperties:['data', 'isStacked'], + + update: function (context) { + + if (this.didChangeFor(context, this.get('displayProperties'))) { + return; + } + var data = this.get('data'); + if (!data || data.length == 0) + return; + + /* + if (!this.get('seriesSet')) { + console.log("I'd rather be bootstrapping"); + } else { + console.log("I'm simply updating"); + } + */ + + var width = this.get('width'); + var height = this.get('height'); + + // Draw the svg and main g element to hold the graph + var svg = d3.selectAll(context) + .selectAll("svg") + .data([data]); + + // Anything done to container only happens the first time it is rendered (after that, enter() will + // be empty. Anything you want to do to existing content must be done to svg + var container = svg.enter().append("svg") + .attr("width", 284) + .attr("height", 120) + .append("g"); + + svg.attr("transform", "translate(" + this.get('leftMarginWidth') + "," + this.get('topMarginHeight') + ")"); + + // Assigns color to layers + var colorScale = this.getPath('parentView.colorScale'); + + var samplesCount = this.get('sampleCount'); + var seriesCount = this.get('seriesCount'); + + // This is the spacing of each group, the domain/range is 0 to the number of samples + // The spacing is the chart width divided by the number of samples, plus .2 for buffer. + var samplesSpacer = d3.scale.ordinal() + .domain(d3.range(this.get('sampleNames').length)) + .rangeRoundBands([20, width], 0.05, 0.05); + + + // This is the spacing within each sample group, the domain/range is 0 to the number of series + // The spacing is the width of each sample divided by number of series. No buffer between them. + var seriesSpacer = d3.scale.ordinal() + .domain(d3.range(seriesCount)) + .rangeRoundBands([0, samplesSpacer.rangeBand()], 0, 0); + + this.set('seriesSpacer', seriesSpacer); + + + // The data with a stack wrapper for optional stacking +// var data = d3.layout.stack()(raw_data); + + var isStacked = this.get('isStacked'); + + var seriesSet = svg.selectAll(".series") + // Bind the 2D matrix. Each outer array is a series. Each series has a datum per Sample + .data(data) + seriesSet.enter().append("g") + .attr("class", "series") + seriesSet.style("fill", function (d, i) { return colorScale(i); }); + + this.set('seriesSet', seriesSet); + + var xScale = this.get('xScale'); + var yScale = this.get('yScale'); + + // The svg div so we can get the right mouse coordinates for the tooltip + + + var commafy = d3.format(",.0f"); + + // Draws bars of each group + var rects = seriesSet.selectAll("rect") + .data(function(d) { return d; }) + rects.enter().append("rect") + .attr("transform", function(d, i) { return "translate(" + samplesSpacer(i) + ", 0)"; }) + .attr("y", function(d) { return yScale(0); }) + rects.attr("width", Math.min(seriesSpacer.rangeBand(), width)); + + var rect = this.set('barSet', rects); + + var isStackable = this.getPath('content.configuration.stackable'); + + var barTooltip = seriesSet.selectAll("rect") + .append('svg:title') + .data(function(d) {return d;}) + .text(function(d) { + + var tooltip_text = commafy(d3.round(d.y)); + + if (isStackable) { + //include the percent in the tooltip if the bars are part of a whole (i.e. "dwelling units + // by type," as opposed to "All Metrics" for which pct doesn't make sense + + perct= d.percent*100; + if (perct != 0) { + formattedPerct = perct.toFixed(0) + "%"; + }else {formattedPerct = ""} + + tooltip_text = d.label + ": " + tooltip_text + " (" + formattedPerct + ")" ; + } + return tooltip_text; }); + + // Places values on bars + seriesSet.selectAll("text") + .data(function(d) { return d; }) + .enter().append("text") + .attr("class", "text") + .attr("transform", function(d, i) { return "translate(" + samplesSpacer(i) + ", 0)"; }) + .attr("x", Math.min(seriesSpacer.rangeBand(), width)/2) + .attr("y", function(d, i) { + return isStacked? + // If stacked, shift the text label so that it's in the center of the bar (y - height) + yScale(d.y + d.y0) + Math.abs(yScale(d.y0) - yScale(d.y0 + d.y))/2 + 7: + // If grouped, position it right above the bar;\ + yScale(d.y); }) + .attr("dy", function(d) { return (d.y >= 0) ? "-0.3em" : "1.1em"; }) + .attr("text-anchor", "middle") + .text(function(d) { + return (d.value > 0) ? + commafy(d3.round(d.value)) : + ""; + }); + + // adding control total guide + // TODO this was creating spurious svg elements, so I shut it off --erictheise + /* this.createControlMarkers(); */ + + // draw x- and y- axes, and horizontal zero line for reference against negatives + container.append("g") + .attr("class", "axis x-axis") + .attr("transform", "translate(0," + height + ")") + .call(this.get('xAxis')); + + container.append("g") + .attr("class", "axis y-axis") + .attr("transform", "translate(" + 25 + ",0)") + .call(this.get('yAxis')); + + container.append("line") + .attr("class", "zero-line") + .attr("x1", 20) + .attr("x2", 300) + .attr("y1", yScale(0)) + .attr("y2", yScale(0)); + + this.openingTransition(); + + }, + + /*** + * The initial growing of the bars from zero when the chart is created + */ + openingTransition: function() { + // The scale based on the maximum bar height + var yScale = this.get('yScale'); + // If the graph starts stacked we have to set different values + var isStacked = this.get('isStacked'); + var height = this.get('height'); + var seriesSpacer = this.get('seriesSpacer'); + + // Transition each bar horizontally + this.get('barSet').transition() + .duration(600) + // Staggers drawing based on series index + .delay(function(d, i) { + return isStacked ? + // Set the height to take into account y0 + 0 : + // Set the target y to the scaled datum value + (600) ; + }) + .attr("y", function(d) { + return yScale( + isStacked ? + // Set the target y to y0 plus the datum value + d3.max([0, d.y0 + d.y]) : + // Set to the scaled version of y + d3.max([0, d.y]) ); + }) + // Set the target height + .attr("height", function(d) { + return isStacked ? + // Set the height to take into account y0 + Math.abs(yScale(d.y0) - yScale(d.y0 + d.y)) : + // Set the target y to the scaled datum value + Math.abs(yScale(d.y) - yScale(0)); + }); + + // If it is going from unstacked to stacked, transition the x values after the y + this.get('seriesSet').transition() + .duration(600) + .delay(function(d, i) { + return isStacked ? + // Set the height to take into account y0 + (600) : + // Set the target y to the scaled datum value + (0); + }) + // Staggers drawing based on series index + .attr("transform", function(d, i) { + return isStacked? + "translate(0, 0)" : + "translate(" + seriesSpacer(i) + ", 0)"; }); + + this.get('seriesSet').selectAll('text') + .transition() + .duration(600) + .delay(function(d, i) { + return isStacked ? + // Set the height to take into account y0 + 0 : + // Set the target y to the scaled datum value + (600) ; + }) + .attr("y", function(d, i) { + return isStacked? + // If stacked, shift the text label so that it's in the center of the bar (y - height) + yScale(d.y + d.y0) + Math.abs(yScale(d.y0) - yScale(d.y0 + d.y))/2 + 7: + // If grouped, position it right above the bar;\ + yScale(d.y); }) + }, + + /*** + * Create the X axis using the sample names + */ + xAxis: function() { + var xlabels = d3.scale.ordinal() + .domain(this.get('sampleNames')) + .rangeRoundBands([0, this.get('width')]); + + return d3.svg.axis() + .scale(xlabels) + .orient("bottom") + .ticks(0) + .tickSize(0, 0, 0) + .tickPadding(10); + }.property('sampleName', 'width'), + + /*** + * Create the Y axis based on the yScale + * @returns {*} + */ + yAxis: function() { + var ylabels = d3.scale.linear() + // This range should extend from either 0 or the minimum y value (if negative) + // to the maximum y value time 1.1 so that axis label is always larger than the maximum value + .domain([d3.min([this.get('yMin'), 0]), d3.max([0, this.get('yMax') * 1.15])]) + .range([this.get('height'), 15]); + + var ystackedlabels = d3.scale.linear() + .domain([d3.min([this.get('yMin'), 0]), this.get('ySum') * 1.15]) + .range([this.get('height'), 15]); + + var isStacked = this.get('isStacked'); + + return d3.svg.axis() + .scale(isStacked ? ystackedlabels : ylabels) + .orient("left") + .ticks(3) + .tickSubdivide(true) + .tickSize(4, 2, 0) + .tickPadding(1) + .tickFormat(d3.format("s")) + }.property('yMin', 'yMax', 'ySum', 'height', 'isStacked'), + + + change: function () { + clearTimeout(timeout); + if (this.value === "grouped") transitionGrouped(); + else transitionStacked(); + }, + + transitionGrouped: function () { + y.domain([0, yGroupMax]); + + rect.transition() + .duration(500) + .delay(function (d, i) { + return i * 10; + }) + .attr("x", function (d, i, j) { + return x(d.x) + x.rangeBand() / n * j; + }) + .attr("width", x.rangeBand() / n) + .transition() + .attr("y", function (d) { + return y(d.y); + }) + .attr("height", function (d) { + return height - y(d.y); + }); + }, + + transitionStacked: function () { + y.domain([0, yStackMax]); + + rect.transition() + .duration(500) + .delay(function (d, i) { + return i * 10; + }) + .attr("y", function (d) { + return y(d.y0 + d.y); + }) + .attr("height", function (d) { + return y(d.y0) - y(d.y0 + d.y); + }) + .transition() + .attr("x", function (d) { + return x(d.x); + }) + .attr("width", seriesSpacer.rangeBand()); + }, + + toString: function() { + return "%@:\n%@".fmt(sc_super(), this.toStringAttributes('isStacked sampleNames sampleCount seriesCount mediumContent yMax yScale xScale width height container seriesSet barSet'.w())); + } + }) +}); + + + + + + +// createControlMarkers: function() { +// var self = this; +// var xScale = this.get('xScale'); +// var yScale = this.get('yScale'); +// +// function getControlTotals(i) { +// return self.getPath('parentView.results').objectAt(i).getPath('configuration.control_totals'); +// } +// return this.get('seriesSet').selectAll("line") +// .data(function(d) {return d;}) +// .enter() +// .append("line") +// .attr("x1", function(d) { return xScale(d.x); }) +// .attr("x2", function(d) { return xScale(d.x) + xScale.rangeBand(); }) +// .attr("y1", function(d, i) { +// return yScale(d3.values(getControlTotals(i))[0]); // TODO just using first control total +// }) +// .attr("y2", function(d, i) { +// return yScale(d3.values(getControlTotals(i))[0]); // TODO just using first control total +// }) +// .style("stroke","black") +// .style("stroke-width", "3"); +// +// }, \ No newline at end of file diff --git a/sproutcore/apps/fp/views/section_toolbars/analytic_toolbar_view.js b/sproutcore/apps/fp/views/section_toolbars/analytic_toolbar_view.js new file mode 100644 index 000000000..e2e4b24e3 --- /dev/null +++ b/sproutcore/apps/fp/views/section_toolbars/analytic_toolbar_view.js @@ -0,0 +1,36 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('views/menu_button_view'); +/*** + * Tests the binding and display of data in view Footprint.ProjectTitlebarView. + */ +Footprint.AnalyticToolbarViewView = Footprint.MenuButtonView.extend({ + classNames: 'footprint-policy-toolbar-view'.w(), + anchorLocation: SC.ANCHOR_TOP, + layout: { height: 32 }, + title: 'Analytic Modules', + + menuItems: function () { + return this.get('defaultMenuItems'); + }.property('defaultMenuItems').cacheable(), + + defaultMenuItems: [ + // View and edit the selected item's attributes + { title: 'Fiscal', action: 'showFiscalModule'}, + { title: 'VMT', action: 'showVMTModule'}, + { title: 'Energy', action: 'showEnergyModule'}, + { title: 'Water', action: 'showWaterModule'} + ], + itemsBinding: SC.Binding.oneWay('.menuItems') +}); diff --git a/sproutcore/apps/fp/views/section_toolbars/layer_toolbar_view.js b/sproutcore/apps/fp/views/section_toolbars/layer_toolbar_view.js new file mode 100644 index 000000000..c490ad5bb --- /dev/null +++ b/sproutcore/apps/fp/views/section_toolbars/layer_toolbar_view.js @@ -0,0 +1,20 @@ +/*** + * Displays a list of titlebars above the scenarios and the various analytic columns. + * Clicking on the titlebars themselves changes the state of the application. The default state is the general view, achieved by clicking on the Scenarios bar. Clicking on an analytical changes to the detail state of that analytical category. + * @type {Class} + */ + + +Footprint.LayerToolbarView = Footprint.EditingToolbarView.extend({ + titleViewLayout: {height: 17}, + controlSize: SC.SMALL_CONTROL_SIZE, + contentBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.content'), + recordType: Footprint.Layer, + activeRecordBinding: SC.Binding.oneWay('Footprint.layerActiveController.content'), + title: 'Layers', + menuItems: [ + SC.Object.create({ title: 'Manage Layers', action: 'doViewLayer' }), + SC.Object.create({ title: 'Export Selected', action: 'doExportRecord' }), + ] +}); + diff --git a/sproutcore/apps/fp/views/section_toolbars/result_toolbar_view.js b/sproutcore/apps/fp/views/section_toolbars/result_toolbar_view.js new file mode 100644 index 000000000..d431de39a --- /dev/null +++ b/sproutcore/apps/fp/views/section_toolbars/result_toolbar_view.js @@ -0,0 +1,13 @@ +/*** + * Displays a list of titlebars above the scenarios and the various analytic columns. + * Clicking on the titlebars themselves changes the state of the application. The default state is the general view, achieved by clicking on the Scenarios bar. Clicking on an analytical changes to the detail state of that analytical category. + * @type {Class} + */ +Footprint.ResultToolbarView = Footprint.EditingToolbarView.extend({ + classNames: 'footprint-result-toolbars-view'.w(), + recordType: Footprint.Layer, + titleViewLayout: {height: 17}, + controlSize: SC.SMALL_CONTROL_SIZE, + title: 'Results', + menuItems: [] +}); diff --git a/sproutcore/apps/fp/views/section_toolbars/scenario_toolbar_view.js b/sproutcore/apps/fp/views/section_toolbars/scenario_toolbar_view.js new file mode 100644 index 000000000..a64603e69 --- /dev/null +++ b/sproutcore/apps/fp/views/section_toolbars/scenario_toolbar_view.js @@ -0,0 +1,60 @@ +/*** + * Displays a list of titlebars above the scenarios and the various analytic columns. + * Clicking on the titlebars themselves changes the state of the application. The default state is the general view, achieved by clicking on the Scenarios bar. Clicking on an analytical changes to the detail state of that analytical category. + * @type {Class} + */ + + +Footprint.ScenarioToolbarView = Footprint.EditingToolbarView.extend({ + titleViewLayout: {left: 0, right: 270, height: 17}, + classNames: "footprint-scenario-toolbar-view".w(), + contentBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.content'), + controlSize: SC.SMALL_CONTROL_SIZE, + // Make the title Scenarios for [property name] + contentNameProperty: 'parent_config_entity.name', + title: 'Scenarios', + titles: SC.Object.create({ + stats1View: 'Population', + stats2View: 'Dwelling Units', + stats3View: 'Employment' + }), + + // TODO stats title bars must be dynamic + childViews: 'titleView stats1View stats2View stats3View'.w(), + + recordType: Footprint.Scenario, + activeRecordBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.content'), + menuItems: [ + SC.Object.create({ title: 'Manage Scenarios', keyEquivalent: 'ctrl_i', action: 'doManageScenarios' }), + SC.Object.create({ isSeparator: YES }), + SC.Object.create({ title: 'Export Selected', keyEquivalent: 'ctrl_e', action: 'doExportRecord', isEnabled:NO }), + SC.Object.create({ title: 'Remove Selected', keyEquivalent: ['ctrl_delete', 'ctrl_backspace'], action: 'doRemoveRecord', isEnabled:NO }) + ], + + stats1View: SC.ToolbarView.extend({ + childViews: ['label'], + layout: {right:180, width: 90, height: 17}, + anchorLocation: SC.ANCHOR_TOP, + label: SC.LabelView.extend({ + valueBinding: SC.Binding.oneWay(parentViewPath(2, '.titles.stats1View')) + }) + }), + stats2View: SC.ToolbarView.extend({ + childViews: ['label'], + layout: {right:90, width: 90, height: 17}, + anchorLocation: SC.ANCHOR_TOP, + label: SC.LabelView.extend({ + valueBinding: SC.Binding.oneWay(parentViewPath(2, '.titles.stats2View')) + }) + }), + stats3View: SC.ToolbarView.extend({ + childViews: ['label'], + layout: {right:0, width: 90, height: 17}, + anchorLocation: SC.ANCHOR_TOP, + layoutBinding: SC.Binding.oneWay(parentViewPath(1, '.layouts.stats3View')), + label: SC.LabelView.extend({ + valueBinding: SC.Binding.oneWay(parentViewPath(2, '.titles.stats3View')) + }) + }) +}); + diff --git a/sproutcore/apps/fp/views/sections/analytic_section_view.js b/sproutcore/apps/fp/views/sections/analytic_section_view.js new file mode 100644 index 000000000..2a7c39810 --- /dev/null +++ b/sproutcore/apps/fp/views/sections/analytic_section_view.js @@ -0,0 +1,42 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/section_toolbars/analytic_toolbar_view'); +sc_require('views/info_views/analytics/fiscal_module_management_view'); + +Footprint.AnalyticSectionView = SC.View.extend({ + classNames: 'footprint-analytic-section-view'.w(), + childViews: 'analyticModuleToolbarView contentView'.w(), + + nowShowingBinding: SC.Binding.oneWay('Footprint.showingAnalyticModuleController.nowShowing'), + + nowShowingView:function() { + return this.getPath('nowShowing') || 'Footprint.FiscalModuleManagementView'; + }.property('nowShowing').cacheable(), + + /** + * Titlebar used to changed the active PolicySet + */ + + analyticModuleToolbarView: Footprint.AnalyticToolbarViewView.extend({ + layout: { height: 24 } + }), + + contentView: SC.ContainerView.extend({ + classNames: 'footprint-analytic-section-content-view'.w(), + layout: {top:24}, + nowShowingBinding: SC.Binding.oneWay('.parentView.nowShowingView') + }) +}); + diff --git a/sproutcore/apps/fp/views/sections/built_form_section_view.js b/sproutcore/apps/fp/views/sections/built_form_section_view.js new file mode 100644 index 000000000..e826e7dc0 --- /dev/null +++ b/sproutcore/apps/fp/views/sections/built_form_section_view.js @@ -0,0 +1,109 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.BuiltFormSectionView = SC.View.extend({ + childViews: 'toolbarView listView overlayView'.w(), + classNames: "footprint-built-form-section-view".w(), + + toolbarView: Footprint.LabelSelectToolbarView.extend({ + classNames: "footprint-built_form-toolbar-view".w(), + titleViewLayout: {height: 24}, + controlSize: SC.REGULAR_CONTROL_SIZE, + icon: sc_static('images/section_toolbars/pulldown.png'), + + contentBinding: 'Footprint.builtFormSetsController.content', + isVisibleBinding: SC.Binding.oneWay('Footprint.builtFormSetsController.status').matchesStatus(SC.Record.READY), + selectionBinding: 'Footprint.builtFormSetsController.selection', + recordType: Footprint.BuiltForm, + activeRecordBinding: SC.Binding.oneWay('Footprint.builtFormActiveController.content'), + + title: null, + itemTitleKey: 'name', + + menuItems: [ + // View and edit the selected item's attributes + { title: 'Manage Built Forms', action: 'doManageBuiltForms'}, + { title: 'Visualize', keyEquivalent: 'ctrl_v', action: 'doVisualize'} + //{ title: 'Export Selected', keyEquivalent: 'ctrl_e', action: 'doExportRecord', isEnabled:NO}, + //{ title: 'Remove Selected', keyEquivalent: ['ctrl_delete', 'ctrl_backspace'], action: 'doRemove', isEnabled:NO}, + ] + }), + + overlayView: Footprint.OverlayView.extend({ + layout: { top: 24 }, + contentBinding: SC.Binding.oneWay('Footprint.builtFormCategoriesTreeController'), + statusBinding:SC.Binding.oneWay('*content.status') + }), + + listView: SC.ScrollView.extend({ + layout: { top: 24 }, + media: null, + // TODO this doesn't make sense + + contentView: SC.SourceListView.extend({ + isEnabledBinding: SC.Binding.oneWay('.content').bool(), + rowHeight: 20, + actOnSelect: NO, + // Dummy value. We just need to tell the view that we have an icon so it calls our renderIcon + contentIconKey: 'medium', + + contentBinding: SC.Binding.oneWay('Footprint.builtFormCategoriesTreeController.arrangedObjects'), + contentValueKey: 'name', + + selectionBinding: SC.Binding.from('Footprint.builtFormCategoriesTreeController.selection'), + + groupExampleView: SC.View.extend(SC.ContentDisplay, { + contentDisplayProperties: ['name'], + render: function(context) { + context = context.begin() + .addClass(['sc-view', 'footprint-built-form-group-view']) + .addClass(this.getPath('theme.classNames')); + context.begin() + .addClass(['sc-view', 'footprint-built-form-group-label-view']) + .addClass(this.getPath('theme.classNames')) + .push(this.getPath('content.name')) + .end(); + context = context.end(); + }, + update: function($context) { + $context.find('.footprint-built-form-group-label-view').text(this.getPath('content.name')); + } + }), + exampleView: SC.View.extend(SC.Control, SC.ContentDisplay, { + classNames: ['footprint-built-form-item'], + contentDisplayProperties: ['name'], + + render: function(context) { + // Color swab + var color = this.getPath('content.medium.content.fill.color'); + context.begin() + .addClass(this.getPath('theme.classNames')) + .addClass(['sc-view', 'footprint-medium-color']) + .setStyle({ 'background-color': color }) + .end(); + // Label + context.begin() + .addClass(this.getPath('theme.classNames')) + .addClass(['sc-view', 'sc-label-view', 'footprint-built-form-item-label-view']) + .push(this.getPath('content.name')) + .end(); + }, + update: function ($context) { + $context.find('.footprint-medium-color').css('background-color', this.getPath('content.medium.content.fill.color')); + $context.find('.footprint-built-form-item-label-view').text(this.getPath('content.name')); + } + }) + }) + }) +}); diff --git a/sproutcore/apps/fp/views/sections/layer_section_view.js b/sproutcore/apps/fp/views/sections/layer_section_view.js new file mode 100644 index 000000000..6d28ca1f5 --- /dev/null +++ b/sproutcore/apps/fp/views/sections/layer_section_view.js @@ -0,0 +1,97 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('views/info_views/presentation/presentation_medium_info_view'); +sc_require('views/sections/section_view'); +sc_require('views/section_toolbars/layer_toolbar_view'); + + +Footprint.LayerSectionView = Footprint.SectionView.extend({ + classNames: ['footprint-layer-section-view'], + + isEnabled: SC.Binding.oneWay('Footprint.layerCategoriesTreeController.status').matchesStatus(SC.Record.READY_CLEAN), + + overlayView: Footprint.OverlayView.extend({ + contentBinding: SC.Binding.from('Footprint.layerCategoriesTreeController'), + statusBinding:SC.Binding.oneWay('Footprint.layerCategoriesTreeController.status') + }), + + toolbarView: Footprint.LayerToolbarView, + + listView: SC.ScrollView.extend({ + layout: {top: 16, bottom: 0}, + + contentView: SC.SourceListView.extend({ + layout: { top: 0 }, + rowHeight: 18, + actOnSelect: NO, + canReorderContent: NO, + showAlternatingRows: YES, + + // An icon indicating the DbEntity type: layer, table, query, view, etc + // Bind to the PresentationMedium instances of the active Scenario's default Library + contentBinding: SC.Binding.from('Footprint.layerCategoriesTreeController.arrangedObjects'), + selectionBinding: SC.Binding.from('Footprint.layerCategoriesTreeController.selection'), + + editControllerContent: null, + editControllerContentBinding: SC.Binding.from('Footprint.layersEditController.content'), + + exampleView: SC.View.extend(SC.Control, { + childViews: ['titleView', 'visibilityView', 'progressOverlayView'], + + editControllerContent: null, + editControllerContentBinding: SC.Binding.from('.parentView.editControllerContent'), + + visibilityView: Footprint.VisibilityPickerView.extend({ + layout: { left: 8, width: 18, height: 18 }, + contentBinding: SC.Binding.oneWay('.parentView*content'), + valueBinding: '.parentView*content.visibility' + }), + titleView: SC.LabelView.extend({ + layout: { left: 34 }, + valueBinding: SC.Binding.oneWay('.parentView*content.name') + }), + progressOverlayView: Footprint.ProgressOverlayForMainStoreView.extend({ + layout: { right: 0, width:100, centerY: 0, height: 16}, + mainStoreContentBinding: SC.Binding.oneWay('.parentView.content'), + nestedStoreContentArrayBinding: SC.Binding.oneWay('.parentView.editControllerContent') + }) + }), + groupExampleView: SC.View.extend(SC.ContentDisplay, { + contentDisplayProperties: ['name'], + render: function(context) { + var title = this.getPath('content.name') || ''; + title = title.titleize(); + var themeClassNames = this.getPath('theme.classNames'); + context = context.begin() + .addClass(themeClassNames) + .addClass(['sc-view', 'footprint-layer-group-view']); + + context.begin() + .addClass(themeClassNames) + .addClass(['sc-view', 'footprint-layer-group-label-view']) + .push(title) + .end(); + + context = context.end(); + }, + update: function($context) { + var title = this.getPath('content.name') || ''; + title = title.titleize(); + $context.find('.footprint-layer-group-label-view').text(title); + } + }) + }) + }) +}); + diff --git a/sproutcore/apps/fp/views/sections/map_section_view.js b/sproutcore/apps/fp/views/sections/map_section_view.js new file mode 100644 index 000000000..c02a2a27b --- /dev/null +++ b/sproutcore/apps/fp/views/sections/map_section_view.js @@ -0,0 +1,397 @@ +sc_require('resources/polymaps'); +sc_require('views/presentation/map/polybrush'); +sc_require('views/presentation/map/map_layer_group'); +sc_require('views/presentation/map/map_controls'); +sc_require('views/presentation/map/map_painting'); +sc_require('views/presentation/map/map_styling'); +sc_require('views/presentation/map/map_tools'); + +$(function() { + window.po = org.polymaps; +}); + +function csrfSafeMethod(method) { + // these HTTP methods do not require CSRF protection + return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); +} +$.ajaxSetup({ + crossDomain: false, // obviates need for sameOrigin test + beforeSend: function (xhr, settings) { + if (!csrfSafeMethod(settings.type)) { + xhr.setRequestHeader("X-CSRFToken", csrftoken); + } + } +}); + +Footprint.MapSectionView = SC.View.extend(Footprint.MapControls, Footprint.MapPainting, SC.ActionSupport, { + + classNames: 'footprint-map-section-view'.w(), + childViews: 'mapView searchView rezoomToProjectExtentView overlayView'.w(), + icon: sc_static('footprint:images/zoom_to_extent.png'), + + /*** + * The Footprint.ConfigEntity or other mappable instance according to which the map is configured + */ + content: null, + contentBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.content'), + + /*** + * All Footprint.Layer instances of the Footprint.ConfigEntity + */ + layers: null, + layersBinding: SC.Binding.oneWay('Footprint.layerLibraryActiveController.layers'), + + /*** + * The Footprint.Layer instance of the content which is active + */ + activeLayerSelection: null, + activeLayerSelectionBinding: SC.Binding.oneWay('Footprint.layerSelectionActiveController.content'), + + map:null, + mapBinding: SC.Binding.oneWay('Footprint.mapController.content'), + activeLayerGroup: null, + activeLayerGroupBinding: SC.Binding.oneWay('Footprint.mapLayerGroupsController.activeLayerGroup'), + + /*** + * The polymaps map instance attached to the map div element + */ + createMap: function() { + var map = po.mcmap() +// .tileSize({X: 512, Y: 512}) + // Set the map container and add the svg element + .container(this.get('mapNode').appendChild(po.svg("svg"))) + // Center the map in the middle of the active ConfigEntity and calculate the zoom based on the bounds + .center(this.get('mapInitialCenter')).extent(this.get('projectExtent')).zoomRange(this.get('mapZoomRange')).zoom(this.get('mapInitialZoom')); + + // Set additional properties on the container + map.container().setAttribute("id", "map-interface"); + map.container().setAttribute("width", "100%"); + map.container().setAttribute("height", "100%"); + + // Enable drag and mouse wheel + map.add(po.compass(map)); + map.add(po.drag(map)); + map.add(po.wheel(map)); + + map.on('move', function() { + Footprint.mapController.refreshMapLayerZoomVisibility() + }); + + // Center to the project bounds + //this.set('configuredCenter', this.get('mapInitialCenter')); + this.onProjectActiveControllerStatus(); + Footprint.mapController.set('content', map); + this.invokeNext(function() { + Footprint.mapController.set('isReady', YES); + }) + }, + + /*** + * The current map zoom level + */ + mapZoom: function () { + return this.get('map').zoom(); + }, + + /*** + * The current center of the map + */ + mapCenter: function () { + return this.get('map').center(); + }, + + /*** + * The height of the map. + * @returns {number} + */ + mapHeight: function () { + return this.mapNode().clientHeight(); + }, + /*** + * The width of the map + * @returns {number} + */ + mapWidth: function () { + return this.mapNode().clientWidth(); + }, + + init: function () { + sc_super(); + + var self = this; + // add hotkeys for map controls + Mousetrap.bind('s', function () { + self.fireAction('doStartSelection'); + }, 'keydown'); + Mousetrap.bind('a', function () { + self.fireAction('doStartSelection'); + }, 'keydown'); + Mousetrap.bind('esc', function () { + self.fireAction('doClearSelection'); + }, 'keydown'); + + // We set the mapTools here since they depend on the view for the map and to send actions to the view + Footprint.mapToolsController.set('content', Footprint.MapTools.create({ + mapView: this + })); + }, + + overlayView: Footprint.OverlayView.extend({ + contentBinding: SC.Binding.oneWay('Footprint.layerCategoriesTreeController.nodes'), + statusBinding:SC.Binding.oneWay('Footprint.layerCategoriesTreeController.status') + }), + + mapView: SC.View.extend({ + classNames: 'footprint-map'.w(), + layout: {left: 0, top: 0, right: 0, bottom: 0}, + readyToCreateMap: null, + readyToCreateMapBinding: SC.Binding.oneWay('Footprint.mapController.readyToCreateMap'), + /*** + * Create the map after this layer becomes ready + */ + didCreateLayer: function () { + SC.Timer.schedule({target: this, action: "afterDelay", interval: 1000}) + }, + afterDelay: function () { + this.addObserver('readyToCreateMap', this, 'createMap'); + this.parentView.createMap(); + }, + createMap: function() { + if (this.get('readyToCreateMap')) { + this.removeObserver('readyToCreateMap', this, 'createMap'); + this.get('parentView').createMap(); + } + } + }), + + searchView: SC.TextFieldView.extend({ + classNames: 'footprint-map-search-view'.w(), + layout: {centerX:0, width:200, top: 5, height:26}, + + // This example adds a search box to a map, using the + // Google Places autocomplete feature. People can enter geographical searches. + // The search box will return a pick list containing + // a mix of places and predicted search terms. + // render: function(context) { + // var context = context.begin(); + // context.push(''); + // context.end(); + // }, + + // update: function() { + // }, + + didCreateLayer: function() { + + // Create the search box and link it to the UI element. + var $input = this.$input(), + input = $input[0]; + + $input.attr('placeholder', 'Search Map'); + + try { + var searchBox = new google.maps.places.SearchBox(input); + var self = this.get('parentView'); + // Listen for the event fired when the user selects an item from the + // pick list. Retrieve the matching places for that item. + google.maps.event.addListener(searchBox, 'places_changed', function() { + var places = searchBox.getPlaces(); + + var place = places[0]; + if (place) { + self.get('map').center({lat:place.geometry.location.lat(), lon:place.geometry.location.lng()}); + } + }); + } + catch(e) { + logWarning("Google Search Box failed to load"); + } + + // Bias the SearchBox results towards places that are within the bounds of the + // current map's viewport. + // TODO need map integration first + /* + google.maps.event.addListener(map, 'bounds_changed', function() { + var bounds = map.getBounds(); + searchBox.setBounds(bounds); + }); + */ + } + }), + + rezoomToProjectExtentView: SC.ButtonView.extend({ + classNames: 'footprint-map footprint-map-rezoom-to-extent-button'.w(), + layout: {left: 27, top: 135, height: 24, width: 20}, + icon: sc_static('footprint:images/zoom_to_extent.png'), + title: null, + action: 'zoomToProjectExtent' + }), + + /*** + * Call this when the activeLayerSelection bounds has been updated to wait for a server update to complete + * When the activeLayerSelection instance is READY_CLEAN the activeLayerGroup.selectionLayer is reloaded. + */ + refreshSelectionLayer: function () { + if (Footprint.mapController.get('selectionLayerNeedsRefresh')) { + Footprint.mapController.set('selectionLayerNeedsRefresh', NO); + if (this.get('activeLayerSelection')) { + // Make this conditional in case the user switched layers in the meantime + // TODO the target layer should be passed in to prevent this problem + this.get('activeLayerSelection').addObserver('status', this, 'reloadSelectionLayerWhenReady'); + this.reloadSelectionLayerWhenReady(); + } + } + }.observes('Footprint.mapController.selectionLayerNeedsRefresh'), + + reloadSelectionLayerWhenReady: function () { + if (this.getPath('activeLayerSelection.status') & SC.Record.READY) { // === SC.Record.READY_CLEAN) { LayerSelection problem + var selectionLayer = this.getPath('activeLayerGroup.selectionLayer') + if (selectionLayer) + selectionLayer.reload(); + this.get('activeLayerSelection').removeObserver('status', this, 'reloadSelectionLayerWhenReady'); + } + }, + + project: null, + projectBinding: SC.Binding.oneWay('*content.parent_config_entity'), + projectExtent: function() { + if (this.get('project')) + return this._polygonBoundingBox(this.getPath('project.bounds.coordinates.firstObject.firstObject')); + }.property('.project').cacheable(), + + resetExtentToProjectExtent: function () { + if (Footprint.mapController.get('mapNeedsRezoomToProject')) { + Footprint.mapController.set('mapNeedsRezoomToProject', NO); + this.extent(this.get('projectExtent')); + } + }.observes('Footprint.mapController.mapNeedsRezoomToProject'), + + layerSelection: null, + layerSelectionBinding: SC.Binding.oneWay('Footprint.layerSelectionActiveController.content'), + /*** + * Returns the layerSelection's current extent. This is a single polygon. + */ + layerSelectionExtent: function() { + if (this.get('layerSelection')) + return this._polygonBoundingBox(this.getPath('layerSelection.selection_extent.coordinates.firstObject')); + }.property('.layerSelection').cacheable(), + + resetExtentToSelectionExtent: function () { + if (Footprint.mapController.get('mapNeedsRezoomToSelection')) { + Footprint.mapController.set('mapNeedsRezoomToSelection', NO); + this.extent(this.get('layerSelectionExtent')); + } + }.observes('Footprint.mapController.mapNeedsRezoomToSelection'), + + + refreshLayer: function () { + if (Footprint.mapController.get('layerNeedsRefresh')) { + Footprint.mapController.set('layerNeedsRefresh', NO); + if (this.getPath('activeLayerGroup')) { + this.getPath('activeLayerGroup.raster').reload(); + this.getPath('activeLayerGroup.vector').reload(); + } + } + }.observes('Footprint.mapController.layerNeedsRefresh'), + + /*** + * The initial center of the map, calculated based on the bounds of the content + * For some reason polymaps needs an initial center in order to set extents. + */ + mapInitialCenter: function() { + return {lat: 38.5450, lon: -121.7394}; + }.property('content').cacheable(), + + + /* + configuredCenter: null, + observeConfiguredCenter:function() { + if (this.get('map') && this.get('configuredCenter')) { + this.center(this.get('configuredCenter')); + } + }.observes('.configuredCenter'), + */ + + /*** + * Sets the center of the map + * @param lat_lon_dict + */ + center: function(lat_lon_dict) { + //this.get('map').center(lat_lon_dict); + }, + + /*** + * Sets the extent of the map + * @param lat_lon_dict + */ + extent: function(bbox) { + this.get('map').extent(bbox); + }, + + /*** + * The zoom range of the map + */ + mapZoomRange: function() { + return [10, 18]; + }.property().cacheable(), + + /*** + * The initial zoom level of the map + * TODO this should be based on the content bounds in the future + */ + mapInitialZoom: function() { + return 16; + }.property().cacheable(), + + /*** + * The div element containing the polymaps map. + * @returns {*} + */ + mapNode: function() { + return this.$('.footprint-map')[0]; + }.property(), + + /*** + * The DOM element where the map is found (used for dblclick and other browser signals to the map) + */ + mapWindow: function() { + return this.get('map').focusableParent(); + }.property('map'), + + /*** + * Reacts to a change in the current project by updating the configuredExtent + */ + onProjectActiveControllerStatus: function() { + if (Footprint.projectActiveController.get('status') === SC.Record.READY_CLEAN) { + this.set('configuredExtent', this.get('projectExtent')); + } + }.observes('Footprint.projectActiveController.status'), + + _polygonCenter: function(coordinates) { + var pairs = []; + for (var i=0; i. + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('views/section_toolbars/result_toolbar_view'); +sc_require('views/presentation/result/chart_view'); +//sc_require('views/info_views/management/analytic_model_management_view'); +//sc_require('views/info_views/management/scenario_development_management_view'); +//sc_require('views/info_views/management/compare_scenarios_management_view'); +sc_require('views/sections/section_view'); + +Footprint.ResultSectionViewCharts = {}; +Footprint.ResultSectionView = Footprint.SectionView.extend({ + + isEnabled: SC.Binding.oneWay('Footprint.resultsController').matchesStatus(SC.Record.READY_CLEAN), + autoResizeStyle: SC.RESIZE_AUTOMATIC, + + content: null, + contentBinding: SC.Binding.oneWay('Footprint.resultsController.content'), + + overlayView: Footprint.OverlayView.extend({ + contentBinding: SC.Binding.from('.parentView.content'), + statusBinding:SC.Binding.oneWay('*content.status') + }), + toolbarView: Footprint.ResultToolbarView.extend({ + controller: Footprint.scenarioActiveController + }), + +// modelManagementView: SC.View.extend({ +// classNames: "footprint-model-management-view".w(), +// layout: {width:435, top: 16}, +// childViews: 'contentView'.w(), +// contentView: SC.TabView.design({ +// classNames: "footprint-model-management-content-view".w(), +// itemTitleKey: 'title', +// itemValueKey: 'value', +// itemIsEnabledKey: 'isEnabled', +// nowShowing: 'Footprint.scenarioDevelopmentManagementView', +// items: [ +// {title: 'Develop/Edit', value: 'Footprint.scenarioDevelopmentManagementView', isEnabled: YES}, +// {title: 'Analyze', value: 'Footprint.analyticModelManagementView', isEnabled: YES}, +// {title: 'Compare', value: 'Footprint.compareScenariosManagementView', isEnabled: YES} +// ], +// nowShowingBinding: '.parentView.nowShowing' +// +// }) +// }), + + listView: SC.ScrollView.extend({ + layout: { top: 16 }, + contentView: SC.View.extend({ + classNames: ['result-view'], + + // Content + allResults: null, + allResultsBinding: SC.Binding.oneWay('Footprint.resultsController.content'), + allResultsStatus: null, + allResultsStatusBinding: SC.Binding.oneWay('Footprint.resultsController.status'), + content: function () { + if (this.get('allResultsStatus') & SC.Record.READY) + return this.get('allResults').filter(function (result) { + return result.getPath('configuration.result_type') == 'bar_graph'; + }); + }.property('allResults', 'allResultsStatus').cacheable(), + + // Child views. Match to content & stack. + childViewLayout: SC.View.HORIZONTAL_STACK, + contentDidChange: function() { + var content = this.get('content') || [], + previousChildViews = SC.clone(this.get('childViews') || []), + childViews = [], + exampleView = this.get('exampleView'); + // Cycle through the content. + var item, child, foundChild, i, j, + lenI = content.get('length'), + lenJ = previousChildViews.get('length'); + for (i = 0; i < lenI; i++) { + item = content.objectAt(i); + foundChild = null; + // For each item, search previousChildViews for an existing one. + for (j = 0; j < lenJ; j++) { + child = previousChildViews.objectAt(j); + if (item === child.get('content')) { + foundChild = child; + break; + } + } + // If no child is found, create one. + if (!foundChild) foundChild = exampleView.create({ content: item }); + // Push the child. + childViews.pushObject(foundChild); + } + // Remove the previous child views and append the new ones. + lenI = previousChildViews.get('length'); + for (i = 0; i < lenI; i++) { + child = previousChildViews.objectAt(i); + this.removeChild(child); + if (!childViews.contains(child)) child.destroy(); + } + lenI = childViews.get('length'); + for (i = 0; i < lenI; i++) { + child = childViews.objectAt(i); + this.appendChild(child); + } + }.observes('content'), + + + /*** + * A 2 Dimensional Bar Chart + * The 'samples' of the chart are Scenarios + * The 'series' of the chart are an accumulated result for each Scenario (e.g. dwelling_units, employment, and population) + * The content o`f the Example is the Result of the active Scenario. It is used to find the Result of the same key of all Scenarios in the scenarios property + * + * Naming conventions: + * One query column in the series, which has a datapoint per sample, is a 'group' + * The default presentation is to put the group samples side by side. + * The alternative presentation is to stack the series of each sample, which is only enabled when the each series result is related (e.g. a series different types of dwelling_units) + * + * Example + * Query columns: du_house__sum, du_apt__sum, du_condo__sum, i.e. the aggregate dwelling units of each housing type + * Samples: 'smart (a)','trend (b)','dumb (c)' + * Series: 'sum du house (x)','sum du apt (y)','sum du condo (z)', + * + * Grouped Presentation (Default): + * x y + * xx y + * xxx yyy zzz + * ___________ + * abc abc abc + * + * Note that each series' items are separated and the common series datum of each sample grouped + * + * Stacked Presentation + * + * z + * z y z + * y x y + * y x x + * x x x + * ________ + * a b c + * + * Note that the series' items are together and the series of each sample are stacked. + */ + + exampleView: SC.View.extend({ + layout: { width: 350, borderRight: 1 }, + classNames: ['results-chart-example-container'], + // Note: legendView and radioButtonView are isolated in the mixins TODO change to view classes + childViews: 'labelView chartView chartStackingToggleView'.w(), + + isStacked: null, + isStackedBinding: SC.Binding.oneWay('*content.configuration').transform(function (value) { + // @TODO The API should return one boolean, but sometimes it returns an array of one boolean + return Array.isArray(value.is_stacked) ? value.is_stacked[0] : value.is_stacked; + }), + + isStackable: null, + isStackableBinding: SC.Binding.oneWay('*content.configuration').transform(function (value) { + return value.stackable; + }), + + // The Chart title View + labelView: SC.LabelView.extend({ + layout: { height: 25 }, + tagName: 'h1', + classNames: ['results-chart-title'], + valueBinding: SC.Binding.oneWay('.parentView*content.name') + }), + + chartStackingToggleView: SC.CheckboxView.extend({ + layout: { left: 5, width: 75, height: 18, top: 5 }, + isVisibleBinding:SC.Binding.oneWay('.parentView.isStackable'), + classNames: ['result-toggle-view'], + valueBinding: '.parentView.isStacked', + title: 'Stack' + }), + + chartView: Footprint.ChartView.extend({ + contentBinding: SC.Binding.oneWay('.parentView.content'), + resultLibraryKeyBinding: SC.Binding.oneWay('Footprint.resultLibraryActiveController.key'), + + isStackableBinding:SC.Binding.oneWay('.parentView.isStackable'), + isStackedBinding: SC.Binding.from('.parentView.isStacked'), + + // Used to compare multiple scenarios + // TODO this should come from the multiple selected scenarios in the future + scenariosBinding: SC.Binding.oneWay('Footprint.scenarioActiveController.content').transform(function(value) { + return value && [value]; + }) + }) + }) + }) + }) +}); diff --git a/sproutcore/apps/fp/views/sections/scenario_section_view.js b/sproutcore/apps/fp/views/sections/scenario_section_view.js new file mode 100644 index 000000000..ced4ecd23 --- /dev/null +++ b/sproutcore/apps/fp/views/sections/scenario_section_view.js @@ -0,0 +1,106 @@ +sc_require('views/section_toolbars/scenario_toolbar_view'); +sc_require('views/sections/section_view'); +sc_require('views/info_views/analytics/fiscal_model_management_view'); +sc_require('views/config_entity/analytic_bar_view'); + + +Footprint.ScenarioSectionView = SC.View.extend({ + classNames: "footprint-scenario-section-view".w(), + childViews: 'overlayView toolbarView listView'.w(), + + overlayView: Footprint.OverlayView.extend({ + contentBinding: SC.Binding.oneWay('Footprint.scenarioCategoriesTreeController'), + statusBinding:SC.Binding.oneWay('Footprint.scenarioCategoriesTreeController.status') + }), + + toolbarView: Footprint.ScenarioToolbarView.extend({ + layout: { height: 24}, + controller: Footprint.projectsController + }), + + listView: SC.ScrollView.extend({ + layout: { top: 18 }, + contentView: SC.SourceListView.extend({ + isEnabledBinding: SC.Binding.oneWay('.content').bool(), + rowHeight: 20, + isEditable: YES, + actOnSelect: YES, + canEditContent: YES, + canDeleteContent: YES, + canReorderContent: YES, + + contentBinding: SC.Binding.oneWay('Footprint.scenarioCategoriesTreeController.arrangedObjects'), + contentValueKey: 'name', + selectionBinding: SC.Binding.from('Footprint.scenarioCategoriesTreeController.selection'), + + // This is used to show progress bar overlays after clone/create/update + editControllerContent:null, + editControllerContentBinding: SC.Binding.from('Footprint.scenariosEditController.content'), + + groupExampleView: SC.View.extend(SC.ContentDisplay, { + contentDisplayProperties: ['name'], + render: function(context) { + var title = this.getPath('content.name') || ''; + title = title.titleize(); + context.begin() + .addClass(this.getPath('theme.classNames')) + .addClass(['sc-view', 'footprint-scenario-group']) + .push(title) + .end(); + }, + update: function($context) { + var title = this.getPath('content.name') || ''; + title = title.titleize(); + $context.find('.footprint-scenario-group').text(title); + } + }), + exampleView: SC.View.extend(SC.Control, { + classNames: ['sc-list-item-view', 'footprint-scenario-item'], + childViews: 'nameView progressOverlayView populationView dwellingUnitsView employmentView'.w(), + + // The view that can be edited by double-clicking + editableChildViewKey: 'nameView', + editControllerContent: null, + editControllerContentBinding: SC.Binding.oneWay('.parentView.editControllerContent'), + + nameView: Footprint.EditableModelStringView.extend({ + isEditable: NO, + layout: {left: 0, right:270, top: 1}, + valueBinding: SC.Binding.oneWay('.parentView*content.name') + }), + + progressOverlayView: Footprint.ProgressOverlayForMainStoreView.extend({ + layout: { left:.5, right: 270, width:.5, centerY: 0, height: 16}, + mainStoreContentBinding: SC.Binding.oneWay('.parentView.content'), + nestedStoreContentArrayBinding: SC.Binding.oneWay('.parentView.editControllerContent') + }), + + // TODO these will be dynamic based on the Result(s) with type 'analytic_bars' + // TODO Find the first Result with result_type=='analytic_bars'. Assume the result has the following + // attribute keys + populationView: Footprint.AnalyticBarView.extend({ + layout: {right:180, width: 90}, + + configEntityBinding: SC.Binding.from('.parentView.content'), + queryAttributeKey: 'population', + isVisibleBinding: SC.Binding.from(".parentView.content").notContentKind(Footprint.TreeItem) + }), + dwellingUnitsView: Footprint.AnalyticBarView.extend({ + layout: {right:90, width: 90}, + + configEntityBinding: SC.Binding.from('.parentView.content'), + queryAttributeKey: 'dwelling_units', + isVisibleBinding: SC.Binding.from(".parentView.content").notContentKind(Footprint.TreeItem) + }), + employmentView: Footprint.AnalyticBarView.extend({ + layout: {right:0, width: 90}, + + configEntityBinding: SC.Binding.from('.parentView.content'), + queryAttributeKey: 'employment', + isVisibleBinding: SC.Binding.from(".parentView.content").notContentKind(Footprint.TreeItem) + }) + }) + }) + }) +}); + diff --git a/sproutcore/apps/fp/views/sections/section_view.js b/sproutcore/apps/fp/views/sections/section_view.js new file mode 100644 index 000000000..0ab8098bd --- /dev/null +++ b/sproutcore/apps/fp/views/sections/section_view.js @@ -0,0 +1,22 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +/** + * Base class for the main application sections. Each section typicall has a titlebar and a main section + * @type {*} + */ +Footprint.SectionView = SC.View.extend({ + classNames: "footprint-section-view".w(), + childViews: 'toolbarView listView overlayView'.w() +}); \ No newline at end of file diff --git a/sproutcore/apps/fp/views/sections/tool_section_view.js b/sproutcore/apps/fp/views/sections/tool_section_view.js new file mode 100644 index 000000000..e25edeebc --- /dev/null +++ b/sproutcore/apps/fp/views/sections/tool_section_view.js @@ -0,0 +1,242 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.ToolSelectionStandardItems = ['Navigate', 'Box', 'Polygon', 'Identify', 'Query']; +Footprint.ToolSectionView = SC.View.extend({ + classNames: "footprint-tool-section-view".w(), + childViews: 'toolbarView navigateAndSelectButtonView featurerBarView'.w(), + + /*** + * The delegate for the active configEntity, used to override settings + */ + configEntityDelegate: null, + + isEnabledBinding: SC.Binding.oneWay('Footprint.layerActiveController.status').matchesStatus(SC.Record.READY_CLEAN), + + /*** + * Bind this to the active layer in the layer library. + * The active layer determines what tools are available + */ + activeLayer: null, + activeLayerBinding: SC.Binding.oneWay('Footprint.layerActiveController.content'), + activeLayerStatus: null, + activeLayerStatusBinding: SC.Binding.oneWay('Footprint.layerActiveController*content.status'), + + /*** + * Configuration of what tools should be available to each layer according to their db_entity_key + * TODO moved to a separate configuration that has client specific stuff. + */ + layerLookup: SC.Object.create({ + 'future_scenario_feature': { + subtitle: 'Scenario Painting', + isEnabledItems: Footprint.ToolSelectionStandardItems.concat(['Apply', 'Clear']) + } + }), + + toolbarView: Footprint.TitleView.extend({ + anchorLocation: SC.ANCHOR_TOP, + layout: { height: 18 }, + classNames: "footprint-title-view".w(), + labelViewLayout: {left:0, right:0}, + title:'Tools', + contentBinding:SC.Binding.oneWay('.parentView.activeLayer') + }), + + activeLayerConfig: function () { + if (this.get('activeLayerStatus') & SC.Record.READY) + return this.getPath('layerLookup.%@'.fmt(this.getPath('activeLayer.db_entity_key'))); + }.property('activeLayer', 'activeLayerStatus').cacheable(), + + /** + * Returns YES if the given item is configured for the active layer and the toolsController says its type is isEnabled + * @param item + * @returns {*|boolean|*} + */ + isItemEnabled: function (item) { + var layerConfig = this.get('activeLayerConfig'); + // Find the optional toolController boolean for this item type + var controllerEnabled = Footprint.toolController.get('%@IsEnabled'.fmt(item.get('type'))); + // Return YES if the layerConfig (or default config) and tool enables the item + return (layerConfig ? layerConfig.isEnabledItems : Footprint.ToolSelectionStandardItems).contains(item.title) && + (typeof(controllerEnabled) == 'undefined' || controllerEnabled); + }, + + navigateAndSelectButtonView: SC.SegmentedView.extend({ + layout: { top: 18, height: 26 }, + selectSegmentWhenTriggeringAction: YES, + itemActionKey: 'action', + itemTitleKey: 'title', + itemKeyEquivalentKey: 'keyEquivalent', + itemValueKey: 'title', + itemIsEnabledKey: 'isEnabled', + + rawItems: [ + // View and edit the selected item's attributes + SC.Object.create({ title: 'Navigate', keyEquivalent: 'ctrl_n', action: 'navigate', isEnabled: YES, type: 'navigator'}), + SC.Object.create({ title: 'Point', keyEquivalent: 'ctrl_p', action: 'paintPoint', isEnabled: NO, type: 'selector'}), + SC.Object.create({ title: 'Box', keyEquivalent: 'ctrl_b', action: 'paintBox', isEnabled: NO, type: 'selector'}), + SC.Object.create({ title: 'Polygon', keyEquivalent: 'ctrl_o', action: 'paintPolygon', isEnabled: NO, type: 'selector', isStatelessButton:YES}), + SC.Object.create({ title: 'Identify', keyEquivalent: 'ctrl_i', action: 'doFeatureIdentify', isEnabled: NO, type: 'featurer', isStatelessButton:YES}), + SC.Object.create({ title: 'Query', keyEquivalent: 'ctrl_q', action: 'doFeatureQuery', isEnabled: NO, type: 'selector', isStatelessButton:YES}) + //{ title: 'Undo', keyEquivalent: ['ctrl_u'], action:'navigateOrSelectUndo', isEnabled:NO}, + //{ title: 'Redo', keyEquivalent: ['ctrl_r'], action:'navigateOrSelectRedo', isEnabled:NO} + ], + activeLayer: null, + activeLayerBinding: SC.Binding.oneWay('.parentView.activeLayer'), + activeLayerStatus: null, + activeLayerStatusBinding: SC.Binding.oneWay('.parentView.activeLayerStatus'), + /** + * The items that may or may not be isEnabled, based on the current activeLayer + * This fires whenever the active layer or its status changes, and depends on the configuration of the layer + * type ('selector', 'featurer', etc) and on any specific configuration for that layer. + */ + items: function () { + return this.get('rawItems').map(function (item) { + return SC.Object.create($.extend({}, + item, + // merge a dict that enables it if it's configured for the active layer, otherwise disables + {isEnabled: this.parentView.isItemEnabled(item)})); + }, this); + }.property('activeLayer', 'activeLayerStatus').cacheable(), + + // Don't allow stateless buttons to remain selected + valueObserver: function () { + var value = this.get('value'); + if (value) { + var item = this.get('items').filter(function (item) { + return item.get('title') == value; + })[0]; + if (item.get('isStatelessButton')) + // Set it back. This will refire the observer once + this.set('value', this._statefulValue); + else + // Update it + this._statefulValue = value; + } + }.observes('.value'), + _statefulValue: null, + + // Trigger a changes to items whenever a relevant toolController boolean changes + toolControllerObserver: function () { + this.propertyDidChange('items'); + }.observes( + 'Footprint.toolController.navigatorIsEnabled', + 'Footprint.toolController.selectorIsEnabled', + 'Footprint.toolController.featurerIsEnabled' + ), + + value: 'Navigate' + }), + + featurerBarView: SC.View.extend({ + layout: { top: 44 }, + childViews: 'param1View param2View toggleView applyView clearView bufferView'.w(), + classNames: ['featurer-bar'], + + param1View: Footprint.SliderInfoView.extend({ + layout: { left: 0.02, width: 0.3, height:43, top: 5}, + classNames: ['featurer-bar-param1'], + valueSymbol: '%', + title: 'Dev Pct', + minimum: 0, + maximum: 100, + step: 1, + rawValue: null, + rawValueBinding: SC.Binding.from('Footprint.paintingController.developmentPercent'), + value: function (propKey, value) { + if (value !== undefined) { + this.set('rawValue', value / 100); + return value; + } + return this.get('rawValue') * 100; + }.property('rawValue').cacheable() + }), + param2View: Footprint.SliderInfoView.extend({ + layout: { left: 0.38, width: 0.3, height:43, top: 5}, + classNames: ['featurer-bar-param2'], + valueSymbol: '%', + title: 'Density Pct', + minimum: 0, + maximum: 100, + step: 1, + rawValue: null, + rawValueBinding: SC.Binding.from('Footprint.paintingController.densityPercent'), + value: function (propKey, value) { + if (value !== undefined) { + this.set('rawValue', value / 100); + return value; + } + return this.get('rawValue') * 100; + }.property('rawValue').cacheable() + }), + toggleView: Footprint.CheckboxInfoView.extend({ + layout: { left: 0.76, width: 0.2, height:43, top:5 }, + classNames: ['featurer-bar-toggle'], + title: 'Full Redev.', + valueBinding: 'Footprint.paintingController.isFullRedevelopment' + }), + applyView: SC.ButtonView.extend({ + layout: { left: 0.8, bottom: 0.02, right: 10, height: 24, border: 1}, + classNames: ['theme-button', 'theme-button-blue'], + classNameBindings: ['isFullRedevelopment:is-full-redevelopment'], // adds the is-editable when isEditable is YES + title: 'Apply', + action: 'doPaintApply', + activeLayer: null, + activeLayerBinding: SC.Binding.oneWay('.parentView.parentView.activeLayer'), + isFullRedevelopment: null, + isFullRedevelopmentBinding: SC.Binding.oneWay('.parentView.toggleView.value'), + isEnabled: function () { + return this.parentView.parentView.isItemEnabled(SC.Object.create({ title: 'Apply', isEnabled: NO, type: 'featurer'})) + }.property('activeLayer', 'toolState').cacheable(), + toolState: null, + toolStateBinding: SC.Binding.oneWay('Footprint.toolController.featurerIsEnabled') + }), + clearView: SC.ButtonView.extend({ + layout: { left: 0.1, right:.7, bottom: 0.02, height: 24, border: 1}, + classNames: ['theme-button', 'theme-button-gold'], + title: 'Clear', + action: 'doPaintClear', + activeLayer: null, + activeLayerBinding: SC.Binding.oneWay('.parentView.parentView.activeLayer'), + isEnabled: function () { + return this.parentView.parentView.isItemEnabled(SC.Object.create({ title: 'Clear', isEnabled: NO, type: 'featurer'})) + }.property('activeLayer', 'toolState').cacheable(), + toolState: null, + toolStateBinding: SC.Binding.oneWay('Footprint.toolController.featurerIsEnabled') + }), + resetButtonView: SC.ButtonView.extend({ + action: 'paintReset', + name: 'Reset' + }), + applyStatusView: SC.ImageView.extend({ + layout: { left: 0.8, right: 10, height: 16, width: 16}, + value: sc_static('images/loader.gif') + + }), + bufferView: SC.SegmentedView.extend({ + layout: { bottom: 0, left:.3, height: 26, right: 0.2 }, + selectSegmentWhenTriggeringAction: NO, + itemActionKey: 'action', + itemTitleKey: 'title', + itemKeyEquivalentKey: 'keyEquivalent', + itemValueKey: 'title', + itemIsEnabledKey: 'isEnabled', + + items: [ + // View and edit the selected item's attributes + SC.Object.create({ title: 'Undo', keyEquivalent: 'ctrl_u', action: 'doPaintUndo', isEnabledBinding: SC.Binding.oneWay('Footprint.layerSelectionActiveController*featureUndoManager.canUndo').bool(), type: 'chronicler'}), + SC.Object.create({ title: 'Redo', keyEquivalent: 'ctrl_r', action: 'doPaintRedo', isEnabledBinding: SC.Binding.oneWay('Footprint.layerSelectionActiveController*featureUndoManager.canRedo').bool(), type: 'chronicler'}) + ] + }) + }) +}); diff --git a/sproutcore/frameworks/footprint/.gitignore b/sproutcore/frameworks/footprint/.gitignore new file mode 100644 index 000000000..549da40a8 --- /dev/null +++ b/sproutcore/frameworks/footprint/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +tmp +frameworks diff --git a/sproutcore/frameworks/footprint/buildfile b/sproutcore/frameworks/footprint/buildfile new file mode 100644 index 000000000..ae8b32e69 --- /dev/null +++ b/sproutcore/frameworks/footprint/buildfile @@ -0,0 +1,16 @@ +# UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +# +# Copyright (C) 2013 Calthorpe Associates +# +# This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with this program. If not, see . +# +# Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + +config :all do |c| + c[:required] = [:'sproutcore', 'sproutcore/table', :'sc-table', :'sproutcore-upload'] +end + diff --git a/sproutcore/frameworks/footprint/core.js b/sproutcore/frameworks/footprint/core.js new file mode 100644 index 000000000..f14edeff8 --- /dev/null +++ b/sproutcore/frameworks/footprint/core.js @@ -0,0 +1,57 @@ +// ========================================================================== +// Project: Footprint +// Copyright: @2012 My Company, Inc. +// ========================================================================== +/*globals Footprint */ +sc_require('system/autonomous_store'); +sc_require('system/object_extensions'); +sc_require('system/state_extensions'); +sc_require('system/many_array_extensions'); +sc_require('system/child_array_extensions'); +sc_require('system/store_extensions'); +sc_require('system/binding_extensions'); +sc_require('system/controller_extensions'); +//sc_require('resources/ZeroClipboard'); + +/** @namespace + + My cool new app. Describe your application. + + @extends SC.Object + */ +//SC.LOG_OBSERVERS = YES; +F = Footprint = SC.Application.create( + /** @scope Footprint.prototype */ { + + NAMESPACE: 'Footprint', + VERSION: '0.1.0', + + // We do this primitive check to flag the development mode. There might be a built-in flag. + isDevelopment:window.location.port==4020, + + // This is your application store. You will use this store to access all + // of your model data. You can also set a data source on this store to + // connect to a backend server. The default setup below connects the store + // to any fixtures you define. + //store: SC.Store.create().from('Footprint.FixturesDataSource') + + // Here is the server connector that replaces the fixtures connector + store: SC.Store.create({ + commitRecordsAutomatically: NO + }).from('Footprint.DataSource') + }); + +// Create a path to static content with a wildcard in it. socket.io.js is just used because it's in the resources +// For some reason SC doesn't just offer the path +Footprint.STATIC_FW = sc_static('socket.io.js').replace('socket.io.js','%@'); +Footprint.VISIBLE = 'visible'; +Footprint.HIDDEN = 'hidden'; +Footprint.SOLO = 'solo'; + +// For copying to the browser copy/paste buffer +// TODO figure out how to load swf files!! +/* +Footprint.clip = new ZeroClipboard({ + moviePath: Footprint.STATIC_FW.fmt('ZeroClipboardFlash.swf') +}); +*/ diff --git a/sproutcore/frameworks/footprint/english.lproj/strings.js b/sproutcore/frameworks/footprint/english.lproj/strings.js new file mode 100644 index 000000000..eccce5a42 --- /dev/null +++ b/sproutcore/frameworks/footprint/english.lproj/strings.js @@ -0,0 +1,15 @@ +// ========================================================================== +// Project: Footprint Strings +// Copyright: @2013 My Company, Inc. +// ========================================================================== +/*globals Footprint */ + +// Place strings you want to localize here. In your app, use the key and +// localize it using "key string".loc(). HINT: For your key names, use the +// english string with an underscore in front. This way you can still see +// how your UI will look and you'll notice right away when something needs a +// localized string added to this file! +// +SC.stringsFor('English', { + // "_String Key": "Localized String" +}); diff --git a/sproutcore/frameworks/footprint/mixins/selected_item.js b/sproutcore/frameworks/footprint/mixins/selected_item.js new file mode 100644 index 000000000..1b4d4b0a2 --- /dev/null +++ b/sproutcore/frameworks/footprint/mixins/selected_item.js @@ -0,0 +1,26 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2013 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +/*** + * Mixin to deduce a selectedItem from an ArrayController + * @type {{content: null, contentBinding: SC.Binding, selectedItem: null, selectedItemBinding: SC.Binding}} +*/ +Footprint.SelectedItem = { + content:null, + selectedItem:null, + selectedItemBinding:SC.Binding.oneWay('*controller.selection').transform(function(value) { + return value.firstObject(); + }) +}; \ No newline at end of file diff --git a/sproutcore/frameworks/footprint/models/base_feature_model.js b/sproutcore/frameworks/footprint/models/base_feature_model.js new file mode 100644 index 000000000..ff004ad77 --- /dev/null +++ b/sproutcore/frameworks/footprint/models/base_feature_model.js @@ -0,0 +1,145 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('models/feature_model'); + +Footprint.BaseFeature = Footprint.Feature.extend({ + + geography_id: SC.Record.attr(String), + built_form: SC.Record.toOne("Footprint.BuiltForm", { + isMaster: YES + }), + + region_lu_code: SC.Record.attr(String), + + sqft_parcel: SC.Record.attr(Number), + acres_parcel: SC.Record.attr(Number), + acres_parcel_res: SC.Record.attr(Number), + acres_parcel_res_detsf_sl: SC.Record.attr(Number), + acres_parcel_res_detsf_ll: SC.Record.attr(Number), + acres_parcel_res_attsf: SC.Record.attr(Number), + acres_parcel_res_mf: SC.Record.attr(Number), + acres_parcel_emp: SC.Record.attr(Number), + acres_parcel_emp_off: SC.Record.attr(Number), + acres_parcel_emp_ret: SC.Record.attr(Number), + acres_parcel_emp_ind: SC.Record.attr(Number), + acres_parcel_emp_ag: SC.Record.attr(Number), + acres_parcel_emp_mixed: SC.Record.attr(Number), + acres_parcel_mixed: SC.Record.attr(Number), + acres_parcel_mixed_w_off: SC.Record.attr(Number), + acres_parcel_mixed_no_off: SC.Record.attr(Number), + acres_parcel_no_use: SC.Record.attr(Number), + + du_occupancy_rate: SC.Record.attr(Number), + hh: SC.Record.attr(Number), + + du: SC.Record.attr(Number), + du_detsf_sl: SC.Record.attr(Number), + du_detsf_ll: SC.Record.attr(Number), + du_attsf: SC.Record.attr(Number), + du_mf: SC.Record.attr(Number), + du_mf2to4: SC.Record.attr(Number), + du_mf5p: SC.Record.attr(Number), + + emp: SC.Record.attr(Number), + emp_ret: SC.Record.attr(Number), + emp_retail_services: SC.Record.attr(Number), + emp_restaurant: SC.Record.attr(Number), + emp_accommodation: SC.Record.attr(Number), + emp_arts_entertainment: SC.Record.attr(Number), + emp_other_services: SC.Record.attr(Number), + emp_off: SC.Record.attr(Number), + emp_office_services: SC.Record.attr(Number), + emp_public_admin: SC.Record.attr(Number), + emp_education: SC.Record.attr(Number), + emp_medical_services: SC.Record.attr(Number), + emp_ind: SC.Record.attr(Number), + emp_manufacturing: SC.Record.attr(Number), + emp_wholesale: SC.Record.attr(Number), + emp_transport_warehousing: SC.Record.attr(Number), + emp_utilities: SC.Record.attr(Number), + emp_construction: SC.Record.attr(Number), + emp_ag: SC.Record.attr(Number), + emp_agriculture: SC.Record.attr(Number), + emp_extraction: SC.Record.attr(Number), + emp_military: SC.Record.attr(Number), + + bldg_sqft_detsf_sl: SC.Record.attr(Number), + bldg_sqft_detsf_ll: SC.Record.attr(Number), + bldg_sqft_attsf: SC.Record.attr(Number), + bldg_sqft_mf2to4: SC.Record.attr(Number), + bldg_sqft_mf5p: SC.Record.attr(Number), + + bldg_sqft_retail_services: SC.Record.attr(Number), + bldg_sqft_restaurant: SC.Record.attr(Number), + bldg_sqft_accommodation: SC.Record.attr(Number), + bldg_sqft_arts_entertainment: SC.Record.attr(Number), + bldg_sqft_other_services: SC.Record.attr(Number), + bldg_sqft_office_services: SC.Record.attr(Number), + bldg_sqft_public_admin: SC.Record.attr(Number), + bldg_sqft_education: SC.Record.attr(Number), + bldg_sqft_medical_services: SC.Record.attr(Number), + bldg_sqft_wholesale: SC.Record.attr(Number), + bldg_sqft_transport_warehousing: SC.Record.attr(Number), + residential_irrigated_sqft: SC.Record.attr(Number), + commercial_irrigated_sqft: SC.Record.attr(Number), + + pop: SC.Record.attr(Number), + pop_male: SC.Record.attr(Number), + pop_female: SC.Record.attr(Number), + pop_avg_age20_64: SC.Record.attr(Number), + pop_female_age20_64: SC.Record.attr(Number), + pop_male_age20_64: SC.Record.attr(Number), + pop_age16_up: SC.Record.attr(Number), + pop_age25_up: SC.Record.attr(Number), + pop_age65_up: SC.Record.attr(Number), + pop_age20_64: SC.Record.attr(Number), + pop_hs_not_comp: SC.Record.attr(Number), + pop_hs_diploma: SC.Record.attr(Number), + pop_some_college: SC.Record.attr(Number), + pop_college_degree: SC.Record.attr(Number), + pop_graduate_degree: SC.Record.attr(Number), + pop_employed: SC.Record.attr(Number), + + hh_inc_00_10: SC.Record.attr(Number), + hh_inc_10_20: SC.Record.attr(Number), + hh_inc_20_30: SC.Record.attr(Number), + hh_inc_30_40: SC.Record.attr(Number), + hh_inc_40_50: SC.Record.attr(Number), + hh_inc_50_60: SC.Record.attr(Number), + hh_inc_60_75: SC.Record.attr(Number), + hh_inc_75_100: SC.Record.attr(Number), + hh_inc_100_125: SC.Record.attr(Number), + hh_inc_125_150: SC.Record.attr(Number), + + hh_inc_150_200: SC.Record.attr(Number), + hh_inc_200p: SC.Record.attr(Number), + hh_avg_vehicles: SC.Record.attr(Number), + hh_avg_size: SC.Record.attr(Number), + hh_agg_inc: SC.Record.attr(Number), + hh_avg_inc: SC.Record.attr(Number), + hh_owner_occ: SC.Record.attr(Number), + hh_rental_occ: SC.Record.attr(Number) +}); + +Footprint.BaseFeature.mixin({ + priorityProperties: function () { + return ['built_form', 'region_lu_code', 'pop', 'du', 'emp']; + }, + excludeProperties: function () { + return ['config_entity', 'wkb_geometry'] + } +}); + + +Footprint.BaseDemographicFeature = Footprint.Feature.extend({}) \ No newline at end of file diff --git a/sproutcore/frameworks/footprint/models/base_parcel_feature_model.js b/sproutcore/frameworks/footprint/models/base_parcel_feature_model.js new file mode 100644 index 000000000..ec0830784 --- /dev/null +++ b/sproutcore/frameworks/footprint/models/base_parcel_feature_model.js @@ -0,0 +1,142 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('models/feature_model'); + +Footprint.BaseParcelFeature = Footprint.Feature.extend({ + + source_id: SC.Record.attr(String), + + built_form: SC.Record.toOne("Footprint.BuiltForm", { + isMaster: YES + }), + + region_lu_code: SC.Record.attr(String), + + sqft_parcel: SC.Record.attr(Number), + acres_parcel: SC.Record.attr(Number), + acres_parcel_res: SC.Record.attr(Number), + acres_parcel_res_detsf_sl: SC.Record.attr(Number), + acres_parcel_res_detsf_ll: SC.Record.attr(Number), + acres_parcel_res_attsf: SC.Record.attr(Number), + acres_parcel_res_mf: SC.Record.attr(Number), + acres_parcel_emp: SC.Record.attr(Number), + acres_parcel_emp_off: SC.Record.attr(Number), + acres_parcel_emp_ret: SC.Record.attr(Number), + acres_parcel_emp_ind: SC.Record.attr(Number), + acres_parcel_emp_ag: SC.Record.attr(Number), + acres_parcel_emp_mixed: SC.Record.attr(Number), + acres_parcel_mixed: SC.Record.attr(Number), + acres_parcel_mixed_w_off: SC.Record.attr(Number), + acres_parcel_mixed_no_off: SC.Record.attr(Number), + acres_parcel_no_use: SC.Record.attr(Number), + + du_occupancy_rate: SC.Record.attr(Number), + hh: SC.Record.attr(Number), + du: SC.Record.attr(Number), + du_detsf_sl: SC.Record.attr(Number), + du_detsf_ll: SC.Record.attr(Number), + du_attsf: SC.Record.attr(Number), + du_mf: SC.Record.attr(Number), + du_mf2to4: SC.Record.attr(Number), + du_mf5p: SC.Record.attr(Number), + + emp: SC.Record.attr(Number), + emp_ret: SC.Record.attr(Number), + emp_retail_services: SC.Record.attr(Number), + emp_restaurant: SC.Record.attr(Number), + emp_accommodation: SC.Record.attr(Number), + emp_arts_entertainment: SC.Record.attr(Number), + emp_other_services: SC.Record.attr(Number), + emp_off: SC.Record.attr(Number), + emp_office_services: SC.Record.attr(Number), + emp_public_admin: SC.Record.attr(Number), + emp_education: SC.Record.attr(Number), + emp_medical_services: SC.Record.attr(Number), + emp_ind: SC.Record.attr(Number), + emp_manufacturing: SC.Record.attr(Number), + emp_wholesale: SC.Record.attr(Number), + emp_transport_warehousing: SC.Record.attr(Number), + emp_utilities: SC.Record.attr(Number), + emp_construction: SC.Record.attr(Number), + emp_ag: SC.Record.attr(Number), + emp_agriculture: SC.Record.attr(Number), + emp_extraction: SC.Record.attr(Number), + emp_military: SC.Record.attr(Number), + + bldg_sqft_detsf_sl: SC.Record.attr(Number), + bldg_sqft_detsf_ll: SC.Record.attr(Number), + bldg_sqft_attsf: SC.Record.attr(Number), + bldg_sqft_mf2to4: SC.Record.attr(Number), + bldg_sqft_mf5p: SC.Record.attr(Number), + + bldg_sqft_retail_services: SC.Record.attr(Number), + bldg_sqft_restaurant: SC.Record.attr(Number), + bldg_sqft_accommodation: SC.Record.attr(Number), + bldg_sqft_arts_entertainment: SC.Record.attr(Number), + bldg_sqft_other_services: SC.Record.attr(Number), + bldg_sqft_office_services: SC.Record.attr(Number), + bldg_sqft_public_admin: SC.Record.attr(Number), + bldg_sqft_education: SC.Record.attr(Number), + bldg_sqft_medical_services: SC.Record.attr(Number), + bldg_sqft_wholesale: SC.Record.attr(Number), + bldg_sqft_transport_warehousing: SC.Record.attr(Number), + residential_irrigated_sqft: SC.Record.attr(Number), + commercial_irrigated_sqft: SC.Record.attr(Number), + + pop: SC.Record.attr(Number), + pop_male: SC.Record.attr(Number), + pop_female: SC.Record.attr(Number), + pop_avg_age20_64: SC.Record.attr(Number), + pop_female_age20_64: SC.Record.attr(Number), + pop_male_age20_64: SC.Record.attr(Number), + pop_age16_up: SC.Record.attr(Number), + pop_age25_up: SC.Record.attr(Number), + pop_age65_up: SC.Record.attr(Number), + pop_age20_64: SC.Record.attr(Number), + pop_hs_not_comp: SC.Record.attr(Number), + pop_hs_diploma: SC.Record.attr(Number), + pop_some_college: SC.Record.attr(Number), + pop_college_degree: SC.Record.attr(Number), + pop_graduate_degree: SC.Record.attr(Number), + pop_employed: SC.Record.attr(Number), + + hh_inc_00_10: SC.Record.attr(Number), + hh_inc_10_20: SC.Record.attr(Number), + hh_inc_20_30: SC.Record.attr(Number), + hh_inc_30_40: SC.Record.attr(Number), + hh_inc_40_50: SC.Record.attr(Number), + hh_inc_50_60: SC.Record.attr(Number), + hh_inc_60_75: SC.Record.attr(Number), + hh_inc_75_100: SC.Record.attr(Number), + hh_inc_100_125: SC.Record.attr(Number), + hh_inc_125_150: SC.Record.attr(Number), + + hh_inc_150_200: SC.Record.attr(Number), + hh_inc_200p: SC.Record.attr(Number), + hh_avg_vehicles: SC.Record.attr(Number), + hh_avg_size: SC.Record.attr(Number), + hh_agg_inc: SC.Record.attr(Number), + hh_avg_inc: SC.Record.attr(Number), + hh_owner_occ: SC.Record.attr(Number), + hh_rental_occ: SC.Record.attr(Number) +}); + +Footprint.BaseParcelFeature.mixin({ + priorityProperties: function () { + return ['geography_id', 'built_form', 'region_lu_code', 'pop', 'du', 'emp']; + }, + excludeProperties: function () { + return ['config_entity', 'geometry'] + } +}); diff --git a/sproutcore/frameworks/footprint/models/client_land_use_definition_model.js b/sproutcore/frameworks/footprint/models/client_land_use_definition_model.js new file mode 100644 index 000000000..a48861040 --- /dev/null +++ b/sproutcore/frameworks/footprint/models/client_land_use_definition_model.js @@ -0,0 +1,29 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/footprint_record'); + +Footprint.ClientLandUseDefinition = Footprint.Record.extend({ + land_use: SC.Record.attr(String) +}); + + +Footprint.ClientLandUseDefinition.mixin({ + priorityProperties: function () { + return ['land_use_definition', 'du12', 'emp12', 'acres', 'census_block']; + }, + excludeProperties: function () { + return ['config_entity', 'geometry'] + } +}); diff --git a/sproutcore/frameworks/footprint/models/cpad_holdings_feature_model.js b/sproutcore/frameworks/footprint/models/cpad_holdings_feature_model.js new file mode 100644 index 000000000..8146a5ca3 --- /dev/null +++ b/sproutcore/frameworks/footprint/models/cpad_holdings_feature_model.js @@ -0,0 +1,53 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/feature_model'); + +Footprint.CpadHoldingsFeature = Footprint.Feature.extend({ + + agency_name: SC.Record.attr(String), + unit_name: SC.Record.attr(String), + access_type: SC.Record.attr(String), + acres: SC.Record.attr(Number), + county: SC.Record.attr(String), + agency_level: SC.Record.attr(String), + agency_website: SC.Record.attr(String), + site_website: SC.Record.attr(String), + layer: SC.Record.attr(String), + management_agency: SC.Record.attr(String), + label_name: SC.Record.attr(String), + ownership_type: SC.Record.attr(String), + site_name: SC.Record.attr(String), + alternate_site_name: SC.Record.attr(String), + land_water: SC.Record.attr(String), + special_use: SC.Record.attr(String), + hold_notes: SC.Record.attr(String), + city: SC.Record.attr(String), + + desg_agncy: SC.Record.attr(String), + desg_nat: SC.Record.attr(String), + prim_purp: SC.Record.attr(String), + apn: SC.Record.attr(String), + holding_id: SC.Record.attr(String), + unit_id: SC.Record.attr(String), + + superunit: SC.Record.attr(String), + agency_id: SC.Record.attr(String), + mng_ag_id: SC.Record.attr(String), + al_av_parc: SC.Record.attr(String), + date_revised: SC.Record.attr(String), + src_align: SC.Record.attr(String), + src_attr: SC.Record.attr(String), + d_acq_yr: SC.Record.attr(String) +}); \ No newline at end of file diff --git a/sproutcore/frameworks/footprint/models/default_delegate.js b/sproutcore/frameworks/footprint/models/default_delegate.js new file mode 100644 index 000000000..533ad1a52 --- /dev/null +++ b/sproutcore/frameworks/footprint/models/default_delegate.js @@ -0,0 +1,27 @@ +/** + * + * Created by calthorpe on 11/4/13. + */ + +Footprint.DefaultDelegate = SC.Object.extend({ + dbEntityKeyToFeatureRecordType: function() { + return SC.Object.create({ + base_feature: Footprint.BaseFeature, + cpad_holdings: Footprint.CpadHoldingsFeature, + developable: Footprint.DevelopableFeature, + increments: Footprint.IncrementsFeature, + end_state: Footprint.EndStateFeature, + base_demographic_feature: Footprint.BaseDemographicFeature, + end_state_demographic_feature: Footprint.EndStateDemographicFeature, + future_scenario_feature: Footprint.FutureScenarioFeature, + census_tract: Footprint.CensusTract, + census_blockgroup: Footprint.CensusBlockgroup, + census_block: Footprint.CensusBlock, + vmt_feature: Footprint.VmtFeature + }); + }.property().cacheable(), + + loadingRegionStateClass: function() { + return null; + }.property().cacheable() +}); diff --git a/sproutcore/frameworks/footprint/models/developable_feature_model.js b/sproutcore/frameworks/footprint/models/developable_feature_model.js new file mode 100644 index 000000000..0c4bb35eb --- /dev/null +++ b/sproutcore/frameworks/footprint/models/developable_feature_model.js @@ -0,0 +1,85 @@ + /* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('models/feature_model'); + +Footprint.DevelopableFeature = Footprint.Feature.extend({ + acres_parcel: SC.Record.attr(Number), + acres_parcel_res: SC.Record.attr(Number), + acres_parcel_res_detsf: SC.Record.attr(Number), + acres_parcel_res_detsf_sl: SC.Record.attr(Number), + acres_parcel_res_detsf_ll: SC.Record.attr(Number), + acres_parcel_res_attsf: SC.Record.attr(Number), + acres_parcel_res_mf: SC.Record.attr(Number), + acres_parcel_emp: SC.Record.attr(Number), + acres_parcel_emp_off: SC.Record.attr(Number), + acres_parcel_emp_ret: SC.Record.attr(Number), + acres_parcel_emp_ind: SC.Record.attr(Number), + acres_parcel_emp_ag: SC.Record.attr(Number), + acres_parcel_emp_mixed: SC.Record.attr(Number), + acres_parcel_mixed: SC.Record.attr(Number), + acres_parcel_mixed_w_off: SC.Record.attr(Number), + acres_parcel_mixed_no_off: SC.Record.attr(Number), + acres_parcel_no_use: SC.Record.attr(Number), + + hh: SC.Record.attr(Number), + du: SC.Record.attr(Number), + du_detsf: SC.Record.attr(Number), + du_detsf_sl: SC.Record.attr(Number), + du_detsf_ll: SC.Record.attr(Number), + du_attsf: SC.Record.attr(Number), + du_mf: SC.Record.attr(Number), + + + emp: SC.Record.attr(Number), + emp_ret: SC.Record.attr(Number), + emp_retail_services: SC.Record.attr(Number), + emp_restaurant: SC.Record.attr(Number), + emp_accommodation: SC.Record.attr(Number), + emp_arts_entertainment: SC.Record.attr(Number), + emp_other_services: SC.Record.attr(Number), + emp_off: SC.Record.attr(Number), + emp_office_services: SC.Record.attr(Number), + emp_public_admin: SC.Record.attr(Number), + emp_education: SC.Record.attr(Number), + emp_medical_services: SC.Record.attr(Number), + emp_ind: SC.Record.attr(Number), + emp_manufacturing: SC.Record.attr(Number), + emp_wholesale: SC.Record.attr(Number), + emp_transport_warehousing: SC.Record.attr(Number), + emp_utilities: SC.Record.attr(Number), + emp_construction: SC.Record.attr(Number), + emp_ag: SC.Record.attr(Number), + emp_agriculture: SC.Record.attr(Number), + emp_extraction: SC.Record.attr(Number), + emp_military: SC.Record.attr(Number), + + bldg_sqft_detsf_sl: SC.Record.attr(Number), + bldg_sqft_detsf_ll: SC.Record.attr(Number), + bldg_sqft_attsf: SC.Record.attr(Number), + bldg_sqft_mf: SC.Record.attr(Number), + + bldg_sqft_retail_services: SC.Record.attr(Number), + bldg_sqft_restaurant: SC.Record.attr(Number), + bldg_sqft_accommodation: SC.Record.attr(Number), + bldg_sqft_arts_entertainment: SC.Record.attr(Number), + bldg_sqft_other_services: SC.Record.attr(Number), + bldg_sqft_office_services: SC.Record.attr(Number), + bldg_sqft_public_admin: SC.Record.attr(Number), + bldg_sqft_education: SC.Record.attr(Number), + bldg_sqft_medical_services: SC.Record.attr(Number), + bldg_sqft_wholesale: SC.Record.attr(Number), + bldg_sqft_transport_warehousing: SC.Record.attr(Number), + residential_irrigated_sqft: SC.Record.attr(Number), + commercial_irrigated_sqft: SC.Record.attr(Number) +}); diff --git a/sproutcore/frameworks/footprint/models/end_state_feature_model.js b/sproutcore/frameworks/footprint/models/end_state_feature_model.js new file mode 100644 index 000000000..1064e360b --- /dev/null +++ b/sproutcore/frameworks/footprint/models/end_state_feature_model.js @@ -0,0 +1,97 @@ +/* + *UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + *Copyright (C) 2013 Calthorpe Associates + * + *This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + *This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + *You should have received a copy of the GNU General Public License along with this program. If not, see . + * + *Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +Footprint.EndStateFeature = Footprint.Feature.extend({ + + built_form: SC.Record.toOne("Footprint.BuiltForm", { + isMaster: YES + }), + acres_parcel: SC.Record.attr(Number), + acres_parcel_res: SC.Record.attr(Number), + acres_parcel_res_detsf: SC.Record.attr(Number), + acres_parcel_res_detsf_sl: SC.Record.attr(Number), + acres_parcel_res_detsf_ll: SC.Record.attr(Number), + acres_parcel_res_attsf: SC.Record.attr(Number), + acres_parcel_res_mf: SC.Record.attr(Number), + acres_parcel_emp: SC.Record.attr(Number), + acres_parcel_emp_off: SC.Record.attr(Number), + acres_parcel_emp_ret: SC.Record.attr(Number), + acres_parcel_emp_ind: SC.Record.attr(Number), + acres_parcel_emp_ag: SC.Record.attr(Number), + acres_parcel_emp_mixed: SC.Record.attr(Number), + acres_parcel_mixed: SC.Record.attr(Number), + acres_parcel_mixed_w_off: SC.Record.attr(Number), + acres_parcel_mixed_no_off: SC.Record.attr(Number), + acres_parcel_no_use: SC.Record.attr(Number), + + hh: SC.Record.attr(Number), + du: SC.Record.attr(Number), + du_detsf: SC.Record.attr(Number), + du_detsf_sl: SC.Record.attr(Number), + du_detsf_ll: SC.Record.attr(Number), + du_attsf: SC.Record.attr(Number), + du_mf: SC.Record.attr(Number), + + + emp: SC.Record.attr(Number), + emp_ret: SC.Record.attr(Number), + emp_retail_services: SC.Record.attr(Number), + emp_restaurant: SC.Record.attr(Number), + emp_accommodation: SC.Record.attr(Number), + emp_arts_entertainment: SC.Record.attr(Number), + emp_other_services: SC.Record.attr(Number), + emp_off: SC.Record.attr(Number), + emp_office_services: SC.Record.attr(Number), + emp_public_admin: SC.Record.attr(Number), + emp_education: SC.Record.attr(Number), + emp_medical_services: SC.Record.attr(Number), + emp_ind: SC.Record.attr(Number), + emp_manufacturing: SC.Record.attr(Number), + emp_wholesale: SC.Record.attr(Number), + emp_transport_warehousing: SC.Record.attr(Number), + emp_utilities: SC.Record.attr(Number), + emp_construction: SC.Record.attr(Number), + emp_ag: SC.Record.attr(Number), + emp_agriculture: SC.Record.attr(Number), + emp_extraction: SC.Record.attr(Number), + emp_military: SC.Record.attr(Number), + + bldg_sqft_detsf_sl: SC.Record.attr(Number), + bldg_sqft_detsf_ll: SC.Record.attr(Number), + bldg_sqft_attsf: SC.Record.attr(Number), + bldg_sqft_mf: SC.Record.attr(Number), + + bldg_sqft_retail_services: SC.Record.attr(Number), + bldg_sqft_restaurant: SC.Record.attr(Number), + bldg_sqft_accommodation: SC.Record.attr(Number), + bldg_sqft_arts_entertainment: SC.Record.attr(Number), + bldg_sqft_other_services: SC.Record.attr(Number), + bldg_sqft_office_services: SC.Record.attr(Number), + bldg_sqft_public_admin: SC.Record.attr(Number), + bldg_sqft_education: SC.Record.attr(Number), + bldg_sqft_medical_services: SC.Record.attr(Number), + bldg_sqft_wholesale: SC.Record.attr(Number), + bldg_sqft_transport_warehousing: SC.Record.attr(Number), + residential_irrigated_sqft: SC.Record.attr(Number), + commercial_irrigated_sqft: SC.Record.attr(Number) +}); + +Footprint.EndStateFeature.mixin({ + priorityProperties: function () { + return ['built_form', 'pop', 'du', 'emp']; + }, + excludeProperties: function () { + return ['config_entity', 'geometry'] + } +}); \ No newline at end of file diff --git a/sproutcore/frameworks/footprint/models/feature_model.js b/sproutcore/frameworks/footprint/models/feature_model.js new file mode 100644 index 000000000..756412405 --- /dev/null +++ b/sproutcore/frameworks/footprint/models/feature_model.js @@ -0,0 +1,98 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/footprint_record'); + +Footprint.Feature = Footprint.Record.extend({ + geometry: SC.Record.attr(Object), + config_entity: SC.Record.toOne("Footprint.ConfigEntity", { + isMaster: YES + }) +}); +Footprint.Feature.mixin({ + + /*** + * As far as the API is concerned, all Feature subclasses should be updated and fetched as plain Feature classes + * A layer__id parameter will always accompany them to clarify their type to the server + * @returns {*|RangeObserver|Class|void|Feature} + */ + apiRecordType: function() { + return Footprint.Feature; + }, + + infoPane: function() { + return 'Footprint.FeatureInfoPane'; + }, + + /*** + * Properties that have priority in the info view table + * @returns {Array} + */ + priorityProperties: function () { + return []; + }, + /*** + * Properties to exclude from the info view table + * @returns {Array} + */ + excludeProperties: function () { + return ['config_entity', 'geometry', 'geography']; + }, + /*** + * A Lookup object that maps a property name to a more friendly representation of the instance, such as + * built_form: function(built_form) { return built_form.get('name') }) + * @returns {*} + */ + mapProperties: function () { + return SC.Object.create({ + built_form: function () { + return 'built_form.name'; + }.property(), + land_use_definition: function () { + return 'land_use_definition.land_use'; + }.property(), + census_block: function () { + return 'census_block.block'; + }.property(), + config_entity: function () { + return 'config_entity.name'; + }.property() + }); + } + +}); + +Footprint.CensusTract = Footprint.Feature.extend({ + tract: SC.Record.attr(Number) +}); + +Footprint.CensusBlockgroup = Footprint.Feature.extend({ + blockgroup: SC.Record.attr(Number), + census_tract: SC.Record.toOne("Footprint.CensusTract", { + isMaster: YES, + nested: YES + }) +}); + +Footprint.CensusBlock = Footprint.Feature.extend({ + block: SC.Record.attr(Number), + census_blockgroup: SC.Record.toOne("Footprint.CensusBlockgroup", { + isMaster: YES, + nested: YES + }) +}); + +Footprint.Geography = Footprint.Record.extend({ + source_id: SC.Record.attr(Number) +}); diff --git a/sproutcore/frameworks/footprint/models/footprint_record.js b/sproutcore/frameworks/footprint/models/footprint_record.js new file mode 100644 index 000000000..950040afe --- /dev/null +++ b/sproutcore/frameworks/footprint/models/footprint_record.js @@ -0,0 +1,198 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/footprint_record_cloning'); + + +Footprint.Status = Footprint.Status || {} +Footprint.Record = SC.Record.extend(Footprint.RecordCloning, { + primaryKey: 'id', + + // Used a pseudo-status property to track that cloning of new record's child items are complete + // Each cloned child item will receive this READY_NEW_CLONED status for its _status property + _status:null, + // Used to track progress of saving the record on the server. The value is between 0 and 1 + // This only applies to records like ConfigEntity which do postSave processing on the server + // When records are saved, their progress is set to 0, so saveInProgress will return true + // until post processing brings the value to 1 + progress:null, + saveInProgress: function() { + return this.get('progress') != null && this.get('progress') >= 0 && this.get('progress') < 1; + }.property("progress").cacheable(), + + properties: function() { + return this.get('attributes') ? + $.map(this.get('attributes'), function(value, key) {return key;}) : + []; + }.property('attributes'), + + //TODO local backup of record for editing. We might place this with a call to the server in the future. + //http://www.veebsbraindump.com/2010/10/sproutcore-crud-tutorial-using-sc-tableview/ + /** + * Return an object containing a backup of the properties + * @returns SC.Object object containing properties to backup + */ + backupProperties: function() { + var self = this; + return $.mapObjectToObject( + this.get('attributes'), + function(key, value) { + return [key, self.get(key)]; + }, + function() {return SC.Object.create(); } + ); + }, + + /** + * Restores properties from a backup crated by backupProperties(). + */ + restoreProperties: function(backup) { + backup.forEach(function(key, value) { + this.set(key, backup.get(key)); + }, this); + }, + + // Properties not to copy or clone--typically properties that the server should take care of copying from the source + _skipProperties:function() {return ''.w();}, + // Properties for cloneRecord to copy + _copyProperties:function() {return ''.w();}, + // Properties for cloneRecord to clone (recursive cloneRecord) + // Order doesn't matter unless a _customCloneProperties function makes use of a newly cloned sibling item + _cloneProperties:function() {return ''.w();}, + // Properties that need a custom function to clone them. + // For toOne attributes the function receives as arguments the cloned record, the parent cloned record (or null) and the original property value. + // For toMany attributes the function is called for each item and receives as arguments the cloned record, the parent cloned record (or null), and the original item + _customCloneProperties:function() { return {}; }, + + _singleNonMasterProperties: function() { + var self = this; + return $.grep(this.get('properties'), function(property) { + return self[property] && self[property].kindOf && self[property].kindOf(SC.SingleAttribute) && !self[property].isMaster; + }); + }, + + // Child attributes to save before saving this record + _saveBeforeProperties: function() { return [] }, + // Child attributes to save after saving this record + _saveAfterProperties: function() { return [] }, + + /*** + * Mapping of primitive attributes to other values, Each key/value takes the form: + * key: function(cloneRecord, original record value, random number) { return key+random;} + * where key is the attribute to map and original record value is the corresponding attribute value of the source record. + * random provides a short timestamp for things that should be unique or replaced by the user + **/ + _mapAttributes: { }, + /*** + * Like map attributes but for initialization. No original record value is passed in, so the key/values take the form : + * key: function(cloneRecord, random number) { return "New'+random;} + */ + _initialAttributes: { }, + + // Properties that should never be transfered from one instance to another when updating the values of one instance to those (or the clones) of another + _nonTransferableProperties: function() { return 'id resource_uri'.w(); }, + + // Special actions for setting up a create from "scratch" + // The sourceRecord is used to prime the pump--to give the instance essential attributes like parent references + _createSetup: function(sourceRecord) { + // Initialize preconfigured primitive attributes + var self = this; + var randomNumber = SC.DateTime.create().toFormattedString('%H_%M_%S'); + $.each(this._initialAttributes || {}, function(key, func) { + self.set(key, func(self, randomNumber)) + }); + }, + // Special actions to take when setting up a record for cloning + // that don't involve cloning particular attributes + // For example, a clone might set its origin_instance to the source_record (this should just be standard) + _cloneSetup: function(sourceRecord) { + + }, + /*** + * Sets the record's deleted property to YES. Override to do the same for nested records + * TODO nested records should just be listed as nestedRecords so the crud state knows how + * to create and delete all nested records + * @private + */ + _deleteSetup: function() { + this.set('deleted', YES) + }, + + attributeKeys: function() { + return $.map(this.attributes(), function(v,k) { return k;}); + } +}); + +/*** + * Override this to limit the class name used by the subclasses to a base class name, or to use a custom name + * @returns {string} + */ +SC.mixin(Footprint.Record, { + + generateId: function() { + return -Math.random(Math.floor(Math.random() * 99999999)); + }, + + /*** + * Return a baseclass for certain record types + * @param recordType + * @returns {*} + */ + apiRecordType: function() { + return this; + }, + + /** + * Map the name to somthing else for certain record types, if apiRecordType doesn't take care of it + * @returns {null} + */ + apiClassName: function() { + return null; + }, + + infoPane: function() { + return null; + }, + + allRecordAttributeProperties: function() { + var prototype = this.prototype; + + var filteredProperties = $.map(prototype, function (value, key) { + return value && value.kindOf && value.kindOf(SC.RecordAttribute) ? + key : + null; + }).compact(); + var parentRecordType = prototype.__proto__; + return parentRecordType.allRecordAttributeProperties ? + filteredProperties.concat(parentRecordType.allRecordAttributeProperties()) : + filteredProperties; + + }, + + /*** + * Custom processes of a record's raw dataHash prior to saving + * @param dataHash + */ + processDataHash: function(dataHash, record) { + return dataHash; + } +}); + + +Footprint.ChildRecord = Footprint.Record.extend({ +}); + +Footprint.ChildRecord = Footprint.Record.extend({ +}); + diff --git a/sproutcore/frameworks/footprint/models/footprint_record_cloning.js b/sproutcore/frameworks/footprint/models/footprint_record_cloning.js new file mode 100644 index 000000000..ba732c883 --- /dev/null +++ b/sproutcore/frameworks/footprint/models/footprint_record_cloning.js @@ -0,0 +1,272 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com +*/ + +/**** + * Mixin to Footprint.Record that handles cloning records +*/ +Footprint.RecordCloning = { + + /*** + * Clones this record. Cloning is recursive on any properties listed in _cloneProperties. + * ToOne or ToMany properties should be listed in _cloneProperties or _copyProperties + * Primitive attributes need not be listed. They will be copied in copyAttributes + * cloneRecord must not be called until all deep child attributes are status READY, excepting attributes of + * child objects that are merely being copied by reference. + * @param store + * @param parentClonedRecord: set for recursive calls to identify the parent cloned record. This is used to set isMaster:NO attributes back to the parentRecord + * @returns {*} Returns the clonedRecord with a READY_NEW status. + */ + cloneRecord: function(parentClonedRecord) { + + var newRecord = this.get('store').createRecord( + this.get('store').recordTypeFor(this.storeKey), + {}, + Footprint.Record.generateId()); + + // Do a clone setup for the record to set up attributes not copied from the source + newRecord._cloneSetup(this); + + this._singleNonMasterProperties().forEach(function(property) { + // Assume that any SingleAttribute property that is not master is a reference back to the self. + // Thus set the property to the cloned parent record + // We shouldn't have to set !isMaster + //newRecord.set(property, parentClonedRecord); + }); + + newRecord._cloneOrCopyChildAttributes(parentClonedRecord, this); + + // Copy the primitive attributes + this.copyAttributes(newRecord); + + return newRecord; + }, + + /*** + * Clones this record and transfers the properties to the given target. + * Only _nonTransferableProperties, such as id and resource_id are not transfered + * @param store + * @param target + * @param complete function called on completion of the operation + */ + cloneAndTransferProperties: function(store, target, complete) { + var clonedRecord = this.cloneRecord(store); + $.each(clonedRecord, function(key, value) { + if (!this.get('_nonTransferableProperties').contains(key)) { + target.set(key, value) + } + }); + if (complete) + complete(); + // The top-level clonedRecord has not further utility + clonedRecord.destroy(); + }, + + _copyMany: function(property, value) { + // Handle many attributes + this.get(property).pushObjects(value); + return value; + }, + _copyOne: function(property, value) { + // Handle a one attribute + this.set(property, value); + return value; + }, + _cloneMany: function(property, value, parentClonedRecord) { + var store = this.get('store'); + // Clone each item of the many property normally or with the configured custom function + var itemRecords = value.map(function(item) { + return this._customCloneProperties()[property] ? + this._customCloneProperties()[property](this, parentClonedRecord, item) : + item.cloneRecord(this) + }, this).compact(); + // Only push if no reverse relationship exists + // TODO I don't understand this check, validate it + if (itemRecords.length > 0 && itemRecords[0]._singleNonMasterProperties().length == 0) + this.get(property).pushObjects(itemRecords); + return itemRecords; + }, + _cloneOne: function(property, value, parentClonedRecord) { + var store = this.get('store'); + if (this._customCloneProperties()[property]) + return this._customCloneProperties()[property](this, parentClonedRecord, value); + else { + var clonedItem = value.cloneRecord(this); + this.set(property,clonedItem); + return clonedItem; + } + }, + + /*** + * For a clonedRecord copy or clone properties from the sourceRecord + * @param sourceRecord: Source records whose attributes we're cloning or referencing + * @param parentClonedRecord: For child attribute records, the parent cloned record + * @returns {*} + * @private + */ + _cloneOrCopyChildAttributes: function(parentClonedRecord, sourceRecord) { + // Since we'll combine the copyProperties and clounderstandneProperties in our toProperty function below, create + // a convenient lookup that tells us how to process each by index + var propertyLookup = $.map(this._copyProperties(), function(property) { + return { + property:property, + type:'copy', + one:sourceRecord._copyOne, + many:sourceRecord._copyMany + }; + }).concat($.map(this._cloneProperties(), function(property) { + return { + property:property, + type:'clone', + one:sourceRecord._cloneOne, + many:sourceRecord._cloneMany + }; + })); + propertyLookup.forEach(function(propertyInfo) { + var propertyValue = sourceRecord.get(propertyInfo.property); + Footprint.CloneOrCopy.create({ + sourceRecord:this, + parentClonedRecord:parentClonedRecord, + propertyInfo: propertyInfo, + propertyValue: propertyValue + }); + }, this); + }, + + /** + * Copy any primitive attributes that are not ids and are not returned by _skipProperties(), _copyProperties(), _cloneProperties, nor _singleNonMasterProperties() + * @param record + * @returns {*} + */ + copyAttributes: function(record) { + var self = this; + var randomNumber = SC.DateTime.create().toFormattedString('%H_%M_%S'); + $.each(this.attributes() || {}, function(key, value) { + if (!['id', 'resource_uri'].concat( + self._skipProperties(), + self._copyProperties(), + self._cloneProperties(), + self._singleNonMasterProperties(), + self._nonTransferableProperties(), + $.map(self._customCloneProperties(), function(value, key) {return key;})).contains(key)) + { + record.set(key, self._mapAttributes[key] ? self._mapAttributes[key](record, value, randomNumber) : value); + } + }); + return record; + }, + + /*** + * Recursively load all complex attributes and return a flat list. This is used to check/await statuses + * @param record + */ + loadAttributes: function(_alreadyFound, propertyPath) { + _alreadyFound = _alreadyFound || SC.Set.create(); + propertyPath = propertyPath || []; + + if (_alreadyFound.contains(this)) + return; + else + _alreadyFound.add(this); + + var record = this; + // Combine the keys of non-primitive attributes that need cloning and return the corresponding record value + // Nulls are excluded + var clonePropertyPairs = [].concat( + this._cloneProperties(), + $.map(this._customCloneProperties(), function(value, key) {return key;}) + ).map(function(key) { + var value = record.get(key); + return value ? {key:key, value:value} : null; + }).compact(); + + // Fetch separately the copy by reference attributes. We won't recurse on these + var referencePropertyPairs = [].concat( + this._copyProperties(), + this._singleNonMasterProperties() + ).map(function(key) { + var value = record.get(key); + return value ? {key:key, value:value} : null; + }).compact().filter(function(propertyValue) { + return propertyValue.kindOf && propertyValue.kindOf(Footprint.Record); + }); + + // Recurse on each attribute value if it is itself a record or an enumerable of records + // Use $.map to force a flatten of the inner arrays + // Prepend this record to the result list. + // Nulls are excluded + return [{key:propertyPath.join("."), value:this}].concat(referencePropertyPairs, $.map(clonePropertyPairs, function(propertyPair) { + var propertyValue = propertyPair.value; + var propertyKey = propertyPair.key; + if (propertyValue.kindOf && propertyValue.kindOf(Footprint.Record)) { + // Footprint Record instance + return propertyValue.loadAttributes(_alreadyFound, propertyPath.concat([propertyKey])); + } + else if (propertyValue.isEnumerable) { + // Enumerable + return jQuery.map(propertyValue.toArray(), function(propertyValueItem, i) { + // Possibly Footprint Record instances + if (propertyValueItem.kindOf && propertyValueItem.kindOf(Footprint.Record)) { + return propertyValueItem.loadAttributes(_alreadyFound, propertyPath.concat([propertyKey, i])); + } + }).compact(); + } + }).compact()) + } +}; + + Footprint.CloneOrCopy = SC.Object.extend({ + + clonedRecord:null, + parentClonedRecord: null, + propertyInfo:null, + propertyValue:null, + property:null, + + clonedChildItems:null, + + init: function() { + sc_super(); + this.set('property', this.getPath('propertyInfo.property')); + this.set('clonedChildItems', []); + this.cloneOrCopyProperty(); + }, + + // When the child source attribute record or record array is READY_CLEAN call the many or one function where either copies or clones child property values + // Clones will be asynchronous so we push items to _cloningChildItems and wait for them to complete + cloneOrCopyProperty: function() { + // We need to do this to mark toMany attributes that don't set statuses as complete, namely ChildArray + if (!this.getPath('propertyValue.status')===SC.Record.READY_CLEAN) + this.setPath('propertyValue.status', SC.Record.READY_CLEAN); + + var sourceRecord = this.get('sourceRecord'); + var parentClonedRecord = this.get('parentClonedRecord'); + var property = this.get('property'); + var propertyInfo = this.get('propertyInfo'); + var propertyValue = this.get('propertyValue'); + var clonedChildItems = this.get('clonedChildItems'); + + if (!propertyValue) { + return; + } + if (sourceRecord[property].kindOf(SC.ManyAttribute) || sourceRecord[property].kindOf(SC.ChildrenAttribute)) + propertyInfo.many.apply(sourceRecord, [property, propertyValue, parentClonedRecord]).forEach(function(clonedItem) { + clonedChildItems.push(clonedItem); + }, this); + else { + var clonedItem = propertyInfo.one.apply(sourceRecord, [property, propertyValue, parentClonedRecord]); + clonedChildItems.push(clonedItem); + } + } + }); diff --git a/sproutcore/frameworks/footprint/models/future_scenario_feature_model.js b/sproutcore/frameworks/footprint/models/future_scenario_feature_model.js new file mode 100644 index 000000000..efb559ccc --- /dev/null +++ b/sproutcore/frameworks/footprint/models/future_scenario_feature_model.js @@ -0,0 +1,123 @@ +/* + + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('models/feature_model'); + +Footprint.FutureScenarioFeature = Footprint.Feature.extend({ + + built_form: SC.Record.toOne("Footprint.BuiltForm", { + isMaster: YES + }), + density_pct: SC.Record.attr(Number), + dev_pct: SC.Record.attr(Number), + acres_parcel: SC.Record.attr(Number), + acres_parcel_res: SC.Record.attr(Number), + acres_parcel_res_detsf: SC.Record.attr(Number), + acres_parcel_res_detsf_sl: SC.Record.attr(Number), + acres_parcel_res_detsf_ll: SC.Record.attr(Number), + acres_parcel_res_attsf: SC.Record.attr(Number), + acres_parcel_res_mf: SC.Record.attr(Number), + acres_parcel_emp: SC.Record.attr(Number), + acres_parcel_emp_off: SC.Record.attr(Number), + acres_parcel_emp_ret: SC.Record.attr(Number), + acres_parcel_emp_ind: SC.Record.attr(Number), + acres_parcel_emp_ag: SC.Record.attr(Number), + acres_parcel_emp_mixed: SC.Record.attr(Number), + acres_parcel_mixed: SC.Record.attr(Number), + acres_parcel_mixed_w_off: SC.Record.attr(Number), + acres_parcel_mixed_no_off: SC.Record.attr(Number), + acres_parcel_no_use: SC.Record.attr(Number), + + hh: SC.Record.attr(Number), + du: SC.Record.attr(Number), + du_detsf: SC.Record.attr(Number), + du_detsf_sl: SC.Record.attr(Number), + du_detsf_ll: SC.Record.attr(Number), + du_attsf: SC.Record.attr(Number), + du_mf: SC.Record.attr(Number), + + + emp: SC.Record.attr(Number), + emp_ret: SC.Record.attr(Number), + emp_retail_services: SC.Record.attr(Number), + emp_restaurant: SC.Record.attr(Number), + emp_accommodation: SC.Record.attr(Number), + emp_arts_entertainment: SC.Record.attr(Number), + emp_other_services: SC.Record.attr(Number), + emp_off: SC.Record.attr(Number), + emp_office_services: SC.Record.attr(Number), + emp_public_admin: SC.Record.attr(Number), + emp_education: SC.Record.attr(Number), + emp_medical_services: SC.Record.attr(Number), + emp_ind: SC.Record.attr(Number), + emp_manufacturing: SC.Record.attr(Number), + emp_wholesale: SC.Record.attr(Number), + emp_transport_warehousing: SC.Record.attr(Number), + emp_construction_utilities: SC.Record.attr(Number), + emp_ag: SC.Record.attr(Number), + emp_agriculture: SC.Record.attr(Number), + emp_extraction: SC.Record.attr(Number), + emp_military: SC.Record.attr(Number), + + bldg_sqft_detsf_sl: SC.Record.attr(Number), + bldg_sqft_detsf_ll: SC.Record.attr(Number), + bldg_sqft_attsf: SC.Record.attr(Number), + bldg_sqft_mf: SC.Record.attr(Number), + + bldg_sqft_retail_services: SC.Record.attr(Number), + bldg_sqft_restaurant: SC.Record.attr(Number), + bldg_sqft_accommodation: SC.Record.attr(Number), + bldg_sqft_arts_entertainment: SC.Record.attr(Number), + bldg_sqft_other_services: SC.Record.attr(Number), + bldg_sqft_office_services: SC.Record.attr(Number), + bldg_sqft_public_admin: SC.Record.attr(Number), + bldg_sqft_education: SC.Record.attr(Number), + bldg_sqft_medical_services: SC.Record.attr(Number), + bldg_sqft_wholesale: SC.Record.attr(Number), + bldg_sqft_transport_warehousing: SC.Record.attr(Number), + residential_irrigated_sqft: SC.Record.attr(Number), + commercial_irrigated_sqft: SC.Record.attr(Number) +}); + + +Footprint.FutureScenarioFeature.mixin({ + priorityProperties: function () { + return ['built_form', 'density_pct', 'dev_pct', 'pop', 'du', 'emp']; + }, + excludeProperties: function () { + return ['config_entity', 'wkb_geometry', 'geometry'] + } +}); + + + +Footprint.AnalyticResult = Footprint.Record.extend({ + scenario: SC.Record.toOne('Footprint.Scenario', {isMaster: NO}), + population: SC.Record.attr(Number), + dwelling_units: SC.Record.attr(Number), + employment: SC.Record.attr(Number), + control_total: SC.Record.toMany('Footprint.ControlTotal', {isMaster: NO, nested: YES}), + dwelling_unit_data: SC.Record.toMany('Footprint.DwellingUnitDatum', {isMaster: YES, nested: YES}) +}); + +Footprint.AnalysisModule = Footprint.Record.extend({ + config_entity: SC.Record.toOne('Footprint.ConfigEntity', {isMaster: YES}), + celery_task: SC.Record.attr(Object), + previous_celery_task: SC.Record.attr(Object), + start: SC.Record.attr(Boolean) +}); + +Footprint.Core = Footprint.AnalysisModule.extend({ + +}); diff --git a/sproutcore/frameworks/footprint/models/increment_feature_model.js b/sproutcore/frameworks/footprint/models/increment_feature_model.js new file mode 100644 index 000000000..a8964e847 --- /dev/null +++ b/sproutcore/frameworks/footprint/models/increment_feature_model.js @@ -0,0 +1,53 @@ + /* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ +sc_require('models/feature_model'); + +Footprint.IncrementsFeature = Footprint.Feature.extend({ + land_development_category: SC.Record.attr(String), + refill_flag: SC.Record.attr(Number), + pop: SC.Record.attr(Number), + hh: SC.Record.attr(Number), + du: SC.Record.attr(Number), + du_detsf: SC.Record.attr(Number), + du_detsf_ll: SC.Record.attr(Number), + du_detsf_sl: SC.Record.attr(Number), + du_attsf: SC.Record.attr(Number), + du_mf: SC.Record.attr(Number), + emp: SC.Record.attr(Number), + emp_ret: SC.Record.attr(Number), + emp_retail_services: SC.Record.attr(Number), + emp_restaurant: SC.Record.attr(Number), + emp_accommodation: SC.Record.attr(Number), + emp_arts_entertainment: SC.Record.attr(Number), + emp_other_services: SC.Record.attr(Number), + emp_off: SC.Record.attr(Number), + emp_office_services: SC.Record.attr(Number), + emp_education: SC.Record.attr(Number), + emp_public_admin: SC.Record.attr(Number), + emp_medical_services: SC.Record.attr(Number), + emp_ind: SC.Record.attr(Number), + emp_wholesale: SC.Record.attr(Number), + emp_transport_warehousing: SC.Record.attr(Number), + emp_manufacturing: SC.Record.attr(Number), + emp_utilities: SC.Record.attr(Number), + emp_construction: SC.Record.attr(Number), + emp_ag: SC.Record.attr(Number), + emp_agriculture: SC.Record.attr(Number), + emp_extraction: SC.Record.attr(Number), + emp_military: SC.Record.attr(Number) + +}); + + +Footprint.EndStateDemographicFeature = Footprint.Feature.extend({}) \ No newline at end of file diff --git a/sproutcore/frameworks/footprint/models/primary_parcel_feature_model.js b/sproutcore/frameworks/footprint/models/primary_parcel_feature_model.js new file mode 100644 index 000000000..68dd644cc --- /dev/null +++ b/sproutcore/frameworks/footprint/models/primary_parcel_feature_model.js @@ -0,0 +1,38 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/feature_model'); +sc_require('models/client_land_use_definition_model'); + +Footprint.PrimaryParcelFeature = Footprint.Feature.extend({ + + land_use_definition: SC.Record.toOne("Footprint.ClientLandUseDefinition", { + isMaster: YES + }), + geography: SC.Record.toOne("Footprint.Geography", { + isMaster: YES, + nested: YES + }), + census_block: SC.Record.toOne("Footprint.CensusBlock", { + isMaster: YES, + nested: YES + }) +}); + +// Use this name in all subclasses for the api resource name +SC.mixin(Footprint.PrimaryParcelFeature, { + apiClassName: function() { + return 'existing_land_use_parcel'; + } +}); diff --git a/sproutcore/frameworks/footprint/models/vmt_feature_model.js b/sproutcore/frameworks/footprint/models/vmt_feature_model.js new file mode 100644 index 000000000..95a0ee80f --- /dev/null +++ b/sproutcore/frameworks/footprint/models/vmt_feature_model.js @@ -0,0 +1,42 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2013 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +sc_require('models/feature_model'); + +Footprint.VmtFeature = Footprint.Feature.extend({ + + acres_gross: SC.Record.attr(Number), + pop: SC.Record.attr(Number), + du: SC.Record.attr(Number), + hh: SC.Record.attr(Number), + emp: SC.Record.attr(Number), + final_prod_hbo: SC.Record.attr(Number), + final_prod_hbw: SC.Record.attr(Number), + final_prod_nhb: SC.Record.attr(Number), + final_attr_hbo: SC.Record.attr(Number), + final_attr_hbw: SC.Record.attr(Number), + final_attr_nhb: SC.Record.attr(Number), + vmt_daily: SC.Record.attr(Number), + vmt_daily_w_trucks: SC.Record.attr(Number), + vmt_daily_per_capita: SC.Record.attr(Number), + vmt_daily_per_hh: SC.Record.attr(Number), + vmt_annual: SC.Record.attr(Number), + vmt_annual_w_trucks: SC.Record.attr(Number), + vmt_annual_per_capita: SC.Record.attr(Number), + vmt_annual_per_hh: SC.Record.attr(Number), + raw_trips_total: SC.Record.attr(Number), + internal_capture_trips_total: SC.Record.attr(Number), + walking_trips_total: SC.Record.attr(Number), + transit_trips_total: SC.Record.attr(Number) +}); \ No newline at end of file diff --git a/sproutcore/frameworks/footprint/resources/ZeroClipboard.js b/sproutcore/frameworks/footprint/resources/ZeroClipboard.js new file mode 100644 index 000000000..82ac364aa --- /dev/null +++ b/sproutcore/frameworks/footprint/resources/ZeroClipboard.js @@ -0,0 +1,586 @@ +/*! +* ZeroClipboard +* The ZeroClipboard library provides an easy way to copy text to the clipboard using an invisible Adobe Flash movie and a JavaScript interface. +* Copyright (c) 2013 Jon Rohan, James M. Greene +* Licensed MIT +* http://zeroclipboard.org/ +* v1.2.3 +*/ +(function() { + "use strict"; + var currentElement; + var gluedElements = []; + var flashState = { + global: { + noflash: null, + wrongflash: null, + version: "0.0.0" + }, + clients: {} + }; + var _camelizeCssPropName = function() { + var matcherRegex = /\-([a-z])/g, replacerFn = function(match, group) { + return group.toUpperCase(); + }; + return function(prop) { + return prop.replace(matcherRegex, replacerFn); + }; + }(); + var _getStyle = function(el, prop) { + var value, camelProp, tagName, possiblePointers, i, len; + if (window.getComputedStyle) { + value = window.getComputedStyle(el, null).getPropertyValue(prop); + } else { + camelProp = _camelizeCssPropName(prop); + if (el.currentStyle) { + value = el.currentStyle[camelProp]; + } else { + value = el.style[camelProp]; + } + } + if (prop === "cursor") { + if (!value || value === "auto") { + tagName = el.tagName.toLowerCase(); + possiblePointers = [ "a" ]; + for (i = 0, len = possiblePointers.length; i < len; i++) { + if (tagName === possiblePointers[i]) { + return "pointer"; + } + } + } + } + return value; + }; + var _elementMouseOver = function(event) { + if (!ZeroClipboard.prototype._singleton) return; + if (!event) { + event = window.event; + } + var target; + if (this !== window) { + target = this; + } else if (event.target) { + target = event.target; + } else if (event.srcElement) { + target = event.srcElement; + } + ZeroClipboard.prototype._singleton.setCurrent(target); + }; + var _addEventHandler = function(element, method, func) { + if (element.addEventListener) { + element.addEventListener(method, func, false); + } else if (element.attachEvent) { + element.attachEvent("on" + method, func); + } + }; + var _removeEventHandler = function(element, method, func) { + if (element.removeEventListener) { + element.removeEventListener(method, func, false); + } else if (element.detachEvent) { + element.detachEvent("on" + method, func); + } + }; + var _addClass = function(element, value) { + if (element.addClass) { + element.addClass(value); + return element; + } + if (value && typeof value === "string") { + var classNames = (value || "").split(/\s+/); + if (element.nodeType === 1) { + if (!element.className) { + element.className = value; + } else { + var className = " " + element.className + " ", setClass = element.className; + for (var c = 0, cl = classNames.length; c < cl; c++) { + if (className.indexOf(" " + classNames[c] + " ") < 0) { + setClass += " " + classNames[c]; + } + } + element.className = setClass.replace(/^\s+|\s+$/g, ""); + } + } + } + return element; + }; + var _removeClass = function(element, value) { + if (element.removeClass) { + element.removeClass(value); + return element; + } + if (value && typeof value === "string" || value === undefined) { + var classNames = (value || "").split(/\s+/); + if (element.nodeType === 1 && element.className) { + if (value) { + var className = (" " + element.className + " ").replace(/[\n\t]/g, " "); + for (var c = 0, cl = classNames.length; c < cl; c++) { + className = className.replace(" " + classNames[c] + " ", " "); + } + element.className = className.replace(/^\s+|\s+$/g, ""); + } else { + element.className = ""; + } + } + } + return element; + }; + var _getZoomFactor = function() { + var rect, physicalWidth, logicalWidth, zoomFactor = 1; + if (typeof document.body.getBoundingClientRect === "function") { + rect = document.body.getBoundingClientRect(); + physicalWidth = rect.right - rect.left; + logicalWidth = document.body.offsetWidth; + zoomFactor = Math.round(physicalWidth / logicalWidth * 100) / 100; + } + return zoomFactor; + }; + var _getDOMObjectPosition = function(obj, defaultZIndex) { + var info = { + left: 0, + top: 0, + width: 0, + height: 0, + zIndex: _getSafeZIndex(defaultZIndex) - 1 + }; + if (obj.getBoundingClientRect) { + var rect = obj.getBoundingClientRect(); + var pageXOffset, pageYOffset, zoomFactor; + if ("pageXOffset" in window && "pageYOffset" in window) { + pageXOffset = window.pageXOffset; + pageYOffset = window.pageYOffset; + } else { + zoomFactor = _getZoomFactor(); + pageXOffset = Math.round(document.documentElement.scrollLeft / zoomFactor); + pageYOffset = Math.round(document.documentElement.scrollTop / zoomFactor); + } + var leftBorderWidth = document.documentElement.clientLeft || 0; + var topBorderWidth = document.documentElement.clientTop || 0; + info.left = rect.left + pageXOffset - leftBorderWidth; + info.top = rect.top + pageYOffset - topBorderWidth; + info.width = "width" in rect ? rect.width : rect.right - rect.left; + info.height = "height" in rect ? rect.height : rect.bottom - rect.top; + } + return info; + }; + var _noCache = function(path, options) { + var useNoCache = !(options && options.useNoCache === false); + if (useNoCache) { + return (path.indexOf("?") === -1 ? "?" : "&") + "nocache=" + new Date().getTime(); + } else { + return ""; + } + }; + var _vars = function(options) { + var str = []; + var origins = []; + if (options.trustedOrigins) { + if (typeof options.trustedOrigins === "string") { + origins.push(options.trustedOrigins); + } else if (typeof options.trustedOrigins === "object" && "length" in options.trustedOrigins) { + origins = origins.concat(options.trustedOrigins); + } + } + if (options.trustedDomains) { + if (typeof options.trustedDomains === "string") { + origins.push(options.trustedDomains); + } else if (typeof options.trustedDomains === "object" && "length" in options.trustedDomains) { + origins = origins.concat(options.trustedDomains); + } + } + if (origins.length) { + str.push("trustedOrigins=" + encodeURIComponent(origins.join(","))); + } + if (typeof options.amdModuleId === "string" && options.amdModuleId) { + str.push("amdModuleId=" + encodeURIComponent(options.amdModuleId)); + } + if (typeof options.cjsModuleId === "string" && options.cjsModuleId) { + str.push("cjsModuleId=" + encodeURIComponent(options.cjsModuleId)); + } + return str.join("&"); + }; + var _inArray = function(elem, array) { + if (array.indexOf) { + return array.indexOf(elem); + } + for (var i = 0, length = array.length; i < length; i++) { + if (array[i] === elem) { + return i; + } + } + return -1; + }; + var _prepGlue = function(elements) { + if (typeof elements === "string") throw new TypeError("ZeroClipboard doesn't accept query strings."); + if (!elements.length) return [ elements ]; + return elements; + }; + var _dispatchCallback = function(func, element, instance, args, async) { + if (async) { + window.setTimeout(function() { + func.call(element, instance, args); + }, 0); + } else { + func.call(element, instance, args); + } + }; + var _getSafeZIndex = function(val) { + var zIndex, tmp; + if (val) { + if (typeof val === "number" && val > 0) { + zIndex = val; + } else if (typeof val === "string" && (tmp = parseInt(val, 10)) && !isNaN(tmp) && tmp > 0) { + zIndex = tmp; + } + } + if (!zIndex) { + if (typeof _defaults.zIndex === "number" && _defaults.zIndex > 0) { + zIndex = _defaults.zIndex; + } else if (typeof _defaults.zIndex === "string" && (tmp = parseInt(_defaults.zIndex, 10)) && !isNaN(tmp) && tmp > 0) { + zIndex = tmp; + } + } + return zIndex || 0; + }; + var _deprecationWarning = function(deprecatedApiName, debugEnabled) { + if (deprecatedApiName && debugEnabled !== false && typeof console !== "undefined" && console && (console.warn || console.log)) { + var deprecationWarning = "`" + deprecatedApiName + "` is deprecated. See docs for more info:\n" + " https://github.com/zeroclipboard/zeroclipboard/blob/master/docs/instructions.md#deprecations"; + if (console.warn) { + console.warn(deprecationWarning); + } else { + console.log(deprecationWarning); + } + } + }; + var ZeroClipboard = function(elements, options) { + if (elements) (ZeroClipboard.prototype._singleton || this).glue(elements); + if (ZeroClipboard.prototype._singleton) return ZeroClipboard.prototype._singleton; + ZeroClipboard.prototype._singleton = this; + this.options = {}; + for (var kd in _defaults) this.options[kd] = _defaults[kd]; + for (var ko in options) this.options[ko] = options[ko]; + this.handlers = {}; + if (typeof flashState.global.noflash !== "boolean") { + flashState.global.noflash = !_detectFlashSupport(); + } + if (!flashState.clients.hasOwnProperty(this.options.moviePath)) { + flashState.clients[this.options.moviePath] = { + ready: false + }; + } + if (flashState.global.noflash === false) { + _bridge(); + } + }; + ZeroClipboard.prototype.setCurrent = function(element) { + currentElement = element; + _reposition.call(this); + var titleAttr = element.getAttribute("title"); + if (titleAttr) { + this.setTitle(titleAttr); + } + var useHandCursor = this.options.forceHandCursor === true || _getStyle(element, "cursor") === "pointer"; + _setHandCursor.call(this, useHandCursor); + return this; + }; + ZeroClipboard.prototype.setText = function(newText) { + if (newText && newText !== "") { + this.options.text = newText; + if (this.ready()) this.flashBridge.setText(newText); + } + return this; + }; + ZeroClipboard.prototype.setTitle = function(newTitle) { + if (newTitle && newTitle !== "") this.htmlBridge.setAttribute("title", newTitle); + return this; + }; + ZeroClipboard.prototype.setSize = function(width, height) { + if (this.ready()) this.flashBridge.setSize(width, height); + return this; + }; + var _setHandCursor = function(enabled) { + if (this.ready()) this.flashBridge.setHandCursor(enabled); + }; + var _detectFlashSupport = function() { + var hasFlash = false; + if (typeof flashState.global.noflash === "boolean") { + hasFlash = flashState.global.noflash === false; + } else { + if (typeof ActiveXObject === "function") { + try { + if (new ActiveXObject("ShockwaveFlash.ShockwaveFlash")) { + hasFlash = true; + } + } catch (error) {} + } + if (!hasFlash && navigator.mimeTypes["application/x-shockwave-flash"]) { + hasFlash = true; + } + } + return hasFlash; + }; + ZeroClipboard.version = "1.2.3"; + var _defaults = { + moviePath: "ZeroClipboard.swf", + trustedOrigins: null, + text: null, + hoverClass: "zeroclipboard-is-hover", + activeClass: "zeroclipboard-is-active", + allowScriptAccess: "sameDomain", + useNoCache: true, + forceHandCursor: false, + zIndex: 999999999, + debug: true + }; + ZeroClipboard.setDefaults = function(options) { + for (var ko in options) _defaults[ko] = options[ko]; + }; + ZeroClipboard.destroy = function() { + if (ZeroClipboard.prototype._singleton) { + ZeroClipboard.prototype._singleton.unglue(gluedElements); + var bridge = ZeroClipboard.prototype._singleton.htmlBridge; + if (bridge && bridge.parentNode) { + bridge.parentNode.removeChild(bridge); + } + delete ZeroClipboard.prototype._singleton; + } + }; + var _amdModuleId = null; + var _cjsModuleId = null; + var _bridge = function() { + var flashBridge, len; + var client = ZeroClipboard.prototype._singleton; + var container = document.getElementById("global-zeroclipboard-html-bridge"); + if (!container) { + var opts = {}; + for (var ko in client.options) opts[ko] = client.options[ko]; + opts.amdModuleId = _amdModuleId; + opts.cjsModuleId = _cjsModuleId; + var flashvars = _vars(opts); + var html = ' '; + container = document.createElement("div"); + container.id = "global-zeroclipboard-html-bridge"; + container.setAttribute("class", "global-zeroclipboard-container"); + container.style.position = "absolute"; + container.style.left = "0px"; + container.style.top = "-9999px"; + container.style.width = "15px"; + container.style.height = "15px"; + container.style.zIndex = "" + _getSafeZIndex(client.options.zIndex); + document.body.appendChild(container); + container.innerHTML = html; + } + client.htmlBridge = container; + flashBridge = document["global-zeroclipboard-flash-bridge"]; + if (flashBridge && (len = flashBridge.length)) { + flashBridge = flashBridge[len - 1]; + } + client.flashBridge = flashBridge || container.children[0].lastElementChild; + }; + ZeroClipboard.prototype.resetBridge = function() { + if (this.htmlBridge) { + this.htmlBridge.style.left = "0px"; + this.htmlBridge.style.top = "-9999px"; + this.htmlBridge.removeAttribute("title"); + } + if (currentElement) { + _removeClass(currentElement, this.options.activeClass); + currentElement = null; + } + this.options.text = null; + return this; + }; + ZeroClipboard.prototype.ready = function() { + return flashState.clients[this.options.moviePath].ready === true; + }; + var _reposition = function() { + if (currentElement) { + var pos = _getDOMObjectPosition(currentElement, this.options.zIndex); + this.htmlBridge.style.top = pos.top + "px"; + this.htmlBridge.style.left = pos.left + "px"; + this.htmlBridge.style.width = pos.width + "px"; + this.htmlBridge.style.height = pos.height + "px"; + this.htmlBridge.style.zIndex = pos.zIndex + 1; + this.setSize(pos.width, pos.height); + } + return this; + }; + ZeroClipboard.dispatch = function(eventName, args) { + if (typeof eventName === "string" && eventName) { + var client = ZeroClipboard.prototype._singleton; + var cleanEventName = eventName.toLowerCase().replace(/^on/, ""); + if (cleanEventName) { + _receiveEvent.call(client, cleanEventName, args); + } + } + }; + ZeroClipboard.prototype.on = function(eventName, func) { + var events = eventName.toString().split(/\s/g), added = {}; + for (var i = 0, len = events.length; i < len; i++) { + eventName = events[i].toLowerCase().replace(/^on/, ""); + added[eventName] = true; + if (!this.handlers[eventName]) { + this.handlers[eventName] = func; + } + } + if (added.noflash && flashState.global.noflash) { + _receiveEvent.call(this, "onNoFlash", {}); + } + if (added.wrongflash && flashState.global.wrongflash) { + _receiveEvent.call(this, "onWrongFlash", { + flashVersion: flashState.global.version + }); + } + if (added.load && flashState.clients[this.options.moviePath].ready) { + _receiveEvent.call(this, "onLoad", { + flashVersion: flashState.global.version + }); + } + return this; + }; + ZeroClipboard.prototype.addEventListener = ZeroClipboard.prototype.on; + ZeroClipboard.prototype.off = function(eventName, func) { + var events = eventName.toString().split(/\s/g); + for (var i = 0; i < events.length; i++) { + eventName = events[i].toLowerCase().replace(/^on/, ""); + for (var event in this.handlers) { + if (event === eventName && this.handlers[event] === func) { + delete this.handlers[event]; + } + } + } + return this; + }; + ZeroClipboard.prototype.removeEventListener = ZeroClipboard.prototype.off; + var _receiveEvent = function(eventName, args) { + eventName = eventName.toString().toLowerCase().replace(/^on/, ""); + var element = currentElement; + var performCallbackAsync = true; + switch (eventName) { + case "load": + if (args && args.flashVersion) { + if (!_isFlashVersionSupported(args.flashVersion)) { + _receiveEvent.call(this, "onWrongFlash", { + flashVersion: args.flashVersion + }); + return; + } + flashState.clients[this.options.moviePath].ready = true; + flashState.global.version = args.flashVersion; + } + break; + + case "wrongflash": + if (args && args.flashVersion && !_isFlashVersionSupported(args.flashVersion)) { + flashState.global.wrongflash = true; + flashState.global.version = args.flashVersion; + } + break; + + case "mouseover": + _addClass(element, this.options.hoverClass); + break; + + case "mouseout": + _removeClass(element, this.options.hoverClass); + this.resetBridge(); + break; + + case "mousedown": + _addClass(element, this.options.activeClass); + break; + + case "mouseup": + _removeClass(element, this.options.activeClass); + break; + + case "datarequested": + var targetId = element.getAttribute("data-clipboard-target"), targetEl = !targetId ? null : document.getElementById(targetId); + if (targetEl) { + var textContent = targetEl.value || targetEl.textContent || targetEl.innerText; + if (textContent) { + this.setText(textContent); + } + } else { + var defaultText = element.getAttribute("data-clipboard-text"); + if (defaultText) { + this.setText(defaultText); + } + } + performCallbackAsync = false; + break; + + case "complete": + this.options.text = null; + break; + } + if (this.handlers[eventName]) { + var func = this.handlers[eventName]; + if (typeof func === "string" && typeof window[func] === "function") { + func = window[func]; + } + if (typeof func === "function") { + _dispatchCallback(func, element, this, args, performCallbackAsync); + } + } + }; + ZeroClipboard.prototype.glue = function(elements) { + elements = _prepGlue(elements); + for (var i = 0; i < elements.length; i++) { + if (elements[i] && elements[i].nodeType === 1) { + if (_inArray(elements[i], gluedElements) == -1) { + gluedElements.push(elements[i]); + _addEventHandler(elements[i], "mouseover", _elementMouseOver); + } + } + } + return this; + }; + ZeroClipboard.prototype.unglue = function(elements) { + elements = _prepGlue(elements); + for (var i = 0; i < elements.length; i++) { + _removeEventHandler(elements[i], "mouseover", _elementMouseOver); + var arrayIndex = _inArray(elements[i], gluedElements); + if (arrayIndex != -1) gluedElements.splice(arrayIndex, 1); + } + return this; + }; + function _isFlashVersionSupported(flashVersion) { + return parseFloat(flashVersion.replace(/,/g, ".").replace(/[^0-9\.]/g, "")) >= 10; + } + ZeroClipboard.detectFlashSupport = function() { + var debugEnabled = ZeroClipboard.prototype._singleton && ZeroClipboard.prototype._singleton.options.debug || _defaults.debug; + _deprecationWarning("ZeroClipboard.detectFlashSupport", debugEnabled); + return _detectFlashSupport(); + }; + ZeroClipboard.prototype.setHandCursor = function(enabled) { + _deprecationWarning("ZeroClipboard.prototype.setHandCursor", this.options.debug); + enabled = typeof enabled === "boolean" ? enabled : !!enabled; + _setHandCursor.call(this, enabled); + this.options.forceHandCursor = enabled; + return this; + }; + ZeroClipboard.prototype.reposition = function() { + _deprecationWarning("ZeroClipboard.prototype.reposition", this.options.debug); + return _reposition.call(this); + }; + ZeroClipboard.prototype.receiveEvent = function(eventName, args) { + _deprecationWarning("ZeroClipboard.prototype.receiveEvent", this.options.debug); + if (typeof eventName === "string" && eventName) { + var cleanEventName = eventName.toLowerCase().replace(/^on/, ""); + if (cleanEventName) { + _receiveEvent.call(this, cleanEventName, args); + } + } + }; + if (typeof define === "function" && define.amd) { + define([ "require", "exports", "module" ], function(require, exports, module) { + _amdModuleId = module && module.id || null; + return ZeroClipboard; + }); + } else if (typeof module === "object" && module && typeof module.exports === "object" && module.exports) { + _cjsModuleId = module.id || null; + module.exports = ZeroClipboard; + } else { + window.ZeroClipboard = ZeroClipboard; + } +})(); \ No newline at end of file diff --git a/sproutcore/frameworks/footprint/resources/ZeroClipboardFlash.swf b/sproutcore/frameworks/footprint/resources/ZeroClipboardFlash.swf new file mode 100644 index 000000000..a3aaa2066 Binary files /dev/null and b/sproutcore/frameworks/footprint/resources/ZeroClipboardFlash.swf differ diff --git a/sproutcore/frameworks/footprint/resources/config_entity_delegator.js b/sproutcore/frameworks/footprint/resources/config_entity_delegator.js new file mode 100644 index 000000000..db4e1a495 --- /dev/null +++ b/sproutcore/frameworks/footprint/resources/config_entity_delegator.js @@ -0,0 +1,44 @@ +/** + * Created by calthorpe on 11/4/13. + */ + +/*** + * This mixin is the interface to main-config. + * It resolves the delegate of the configEntity stored in the content property + */ +Footprint.ConfigEntityDelegator = { + + parentConfigEntityDelegator: null, + /*** + * Resolves the delegate for the current configEntity. This climbs the parent_config_entity tree + * until it finds a defined delegate. If none are found it returns the default delegate + */ + configEntityDelegate: function() { + if (!(this.get('content') && this.get('status') & SC.Record.READY)) + return; + if (this.getPath('content.schema')) { + var regionSchema = this.getPath('content.schema').split('__')[0].capitalize(); + var cls = SC.objectForPropertyPath('Footprint%@.%@Delegate'.fmt( + regionSchema, + regionSchema).classify()); + if (cls) + return cls.create(); + } + // We didn't find a module for the given schema. Try the parent schema or default if we are already at region scope + return this.get('parentDelegate'); + }.property('content', 'status').cacheable(), + + parentDelegate: function () { + var parentConfigEntityDelegator = this.get('parentConfigEntityDelegator'); + // Find the configEntity's parent delegate or the default if none exists + var delegate = parentConfigEntityDelegator ? parentConfigEntityDelegator.get('configEntityDelegate') : this.get('defaultDelegate'); + if (!delegate) + throw Error("Delegate in null. This should never happen"); + return delegate; + }.property('content').cacheable(), + + defaultDelegate: function() { + return SC.objectForPropertyPath('Footprint.DefaultDelegate').create(); + }.property('content').cacheable() + +}; diff --git a/sproutcore/frameworks/footprint/resources/debug_utils.js b/sproutcore/frameworks/footprint/resources/debug_utils.js new file mode 100644 index 000000000..e63fb6588 --- /dev/null +++ b/sproutcore/frameworks/footprint/resources/debug_utils.js @@ -0,0 +1,68 @@ +/** + * Created by calthorpe on 12/16/13. + */ + +// TODO these should be mixed into SC.Observable +/*** + * short-cut to getPath + * @param obj + * @param path + * @returns {*} + */ +function g(obj, path) { + return obj.getPath(path); +} +/*** + * short-cut to status + * @param obj + * @param path + * @returns {*} + */ +function s(obj, path) { + return path ? obj.getPath(path + '.status') : obj.get('status'); +} +/*** + * short-cut to constructor + * @param obj + * @param path + * @returns {*} + */ +function c(obj, path) { + return path ? obj.getPath(path + '.constructor') : obj.get('constructor'); +} +/*** + * short-cut to firstObject.toString() + * @param obj + * @param path + * @returns {*} + */ +function f(obj, path) { + return (path ? obj.getPath(path + '.firstObject') : obj.get('firstObject')).toString(); +} +/*** + * short-cut to firstObject.constructor + * @param obj + * @param path + * @returns {*} + */ +function fc(obj, path) { + return (path ? obj.getPath(path + '.firstObject.constuctor') : obj.getPath('firstObject.constructor')); +} +/*** + * short-cut to length + * @param obj + * @param path + * @returns {*} + */ +function l(obj, path) { + return path ? obj.getPath(path + '.length') : obj.get('length'); +} + +function ts(obj, path) { + return path ? obj.getPath(path).toString() : obj.toString(); +} + +function a(record, path) { + return path ? record.getPath(path + '.attributes') : record.get('attributes') +} + diff --git a/sproutcore/frameworks/footprint/resources/fixture_utils.js b/sproutcore/frameworks/footprint/resources/fixture_utils.js new file mode 100644 index 000000000..d447c3d9b --- /dev/null +++ b/sproutcore/frameworks/footprint/resources/fixture_utils.js @@ -0,0 +1,81 @@ + + /* +* UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. +* +* Copyright (C) 2012 Calthorpe Associates +* +* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. +* +* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along with this program. If not, see . +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + + sc_require('resources/jqueryExtensions'); + + // This must mirror the presentation ids in presentation fixtures. Redefined here to avoid circular references + function presentationIds() { + return {maps:[1],results:[2]}; + } + // Clones the given associated id for the given configEntityId. + // The given associated id will be a static id defined in the fixtures, like the presentation fixtures or db_entity_interest fixtures + // Its assumed these are already multipled by 10 or something relative to the base configEntity. So when we pass in the cloned configEntityId here + // we get a unique id + function cloneAssociatedIdForClonedConfigEntity(associatedId, clonedConfigEntityId) { + return associatedId + clonedConfigEntityId*100; + } + function clonePresentationIdsForClonedConfigEntity(configEntityId) { + return $.mapObjectToObject( + presentationIds(), + function(key, presentationIds) { + return [key, jQuery.map(presentationIds, function(presentationId) { + return cloneAssociatedIdForClonedConfigEntity(presentationId, configEntityId); + })]; + }); + } + +Footprint.FIXTURES_MULTIPLIER = 4; +Footprint.FIXTURES_ID_MULTIPLIER = 10; +function multiplyScenarioId(baseId, index) { + return baseId*Footprint.FIXTURES_ID_MULTIPLIER + index; +} +function generatedSortedLettersFromProjectName(scenario) { + return generateSortedLetters( + $.grep(Footprint.Project.FIXTURES, function(p) { + return p.id==scenario.parent_config_entity})[0].name + ) +} +function generateSortedLetters(name) { + return name.toUpperCase().slice(0,Footprint.FIXTURES_MULTIPLIER).split('').sort(); +} + + function multiplyScenarios(scenarios) { + return $.map(scenarios, function(scenario) { + return jQuery.map( + generatedSortedLettersFromProjectName(scenario), + function(name, i) { + var new_scenario = jQuery.extend({}, scenario); + new_scenario.id = multiplyScenarioId(new_scenario.id, i); + //todo temp field + new_scenario.core_analytic_result = new_scenario.id*10 + i; + new_scenario.name += ' %@'.fmt(name); + new_scenario.key += '%@'.fmt(name); + new_scenario.presentations = $.mapObjectToObject( + scenario.presentations, + function(key, presentationList) { + return [key, $.map( + presentationList, + function(presentation) { + return cloneAssociatedIdForClonedConfigEntity(presentation, new_scenario.id); + })]; + }); + new_scenario.db_entity_interests = $.map(scenario.db_entity_interests, function(db_entity_interest) { + return cloneAssociatedIdForClonedConfigEntity(db_entity_interest, new_scenario.id); + }); + return new_scenario; + }); + }) + } + diff --git a/sproutcore/frameworks/footprint/resources/images/spinner.gif b/sproutcore/frameworks/footprint/resources/images/spinner.gif new file mode 100755 index 000000000..39fcb6751 Binary files /dev/null and b/sproutcore/frameworks/footprint/resources/images/spinner.gif differ diff --git a/sproutcore/frameworks/footprint/resources/images/zoom_to_extent.png b/sproutcore/frameworks/footprint/resources/images/zoom_to_extent.png new file mode 100644 index 000000000..d8e4b1e39 Binary files /dev/null and b/sproutcore/frameworks/footprint/resources/images/zoom_to_extent.png differ diff --git a/sproutcore/frameworks/footprint/resources/integration_utils_for_controllers.js b/sproutcore/frameworks/footprint/resources/integration_utils_for_controllers.js new file mode 100644 index 000000000..ba3dcb876 --- /dev/null +++ b/sproutcore/frameworks/footprint/resources/integration_utils_for_controllers.js @@ -0,0 +1,273 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +/*** + * Utility functions for controller integration testing + */ + +/*** + * Sets up the application for controller testing + * @param config. An object with configuration atrributes: + * attribute stateSetup: a no-argument function that initializes any content data and controllers that rely on that content. It also optionally sets the application to a certain state, to avoid going through, the normal statechart flow (e.g. to avoid the login phase by binding the userController and going to the 'loadingApp' state. + * attribute datasource: an optional class to use for the Datasource, such as Footprint.Datasource. Defaults to SC.Record.fixtures + * @return {*} + */ +function setupApplicationForControllerTesting(config) { + + setupForTesting(config); + SC.RunLoop.begin(); + Footprint.statechart.initStatechart(); + config.stateSetup && config.stateSetup(); + SC.RunLoop.end(); +} + + +/*** + * Clears data from the store after tests complete + */ +function controllerTeardown() { + + // For some reason the Result tab is being set to hidden for passed tests + $('.passed').css('display', 'block'); + // Make sure the .core-test div has height! + $('.core-test').height('1000px'); + //SC.RunLoop.begin(); + //Footprint.store.reset(); + //SC.RunLoop.end(); +} + +function testListController(params) { + return Footprint.Test.create({ + dependsOn: params.controllers.get('itemsController'), + + timeout:10000, // dependency must be READY_CLEAN by this time + onComplete: function() { + // Restart the main thread if we finish on time + start(); + }, + + dependents: [ + Footprint.Test.extend({ + label:'listControllerValidation', + run: function() { + // this is synchronous so don't return a value to monitor + listControllerValidation(params); + } + }) + ].concat(listTests(params)) + }); +} + +function testTreeController(params) { + + return Footprint.Test.create({ + dependsOn: params.controllers.get('itemsController'), + + timeout:10000, // dependency must be READY_CLEAN by this time + onComplete: function() { + // Restart the main thread if we finish on time + start(); + }, + + dependents: [ + Footprint.Test.extend({ + label:'treeControllerValidation', + run: function() { + // this is synchronous so don't return a value to monitor + treeControllerValidation(params); + } + }), + ].concat(listTests(params)) + + }); +} + +function listTests(params) { + return [ + Footprint.Test.extend({ + label:'cloneRecordTest', + run: function() { + // Returns the clonedObject, we monitor it's _status property before continuing to validation + return cloneRecordTest(params); + }, + // Await this alternative status property that indicates the clone is complete + // We could alternatively await 'state' Footprint.EDIT_READY_CREATE + completeStatusProperty:'_status', + completeStatus:Footprint.Status.READY_NEW_CLONED + }), + Footprint.Test.extend({ + label:'cloneRecordValidation', + run: function() { + cloneRecordValidation(params); + } + }), + Footprint.Test.extend({ + label:'saveClonedRecordTest', + run: function() { + return saveClonedRecordTest(params); + } + }), + Footprint.Test.extend({ + label:'saveClonedRecordValidation', + run: function() { + saveClonedRecordValidation(params); + } + }), + // Simply calls update_current on the EditController, we perform the update in the next test + // to make sure the object to be edited is loaded, which is asynchronous. + Footprint.Test.extend({ + label:'startUpdatePropertyTest', + run: function() { + return startUpdatePropertyTest(params); + }, + // When the controller is in update mode we're ready + completeStatusProperty:'state', + completeStatus:Footprint.EDIT_READY_UPDATE + }), + Footprint.Test.extend({ + label:'updatePropertyTest', + run: function() { + var property = 'name'; + return updatePropertyTest(params, property, String); + } + }), + Footprint.Test.extend({ + label:'updatePropertyValidation', + run: function() { + var property = 'name'; + updatePropertyValidation(params, property, String); + } + }) + ] + +} + +function listControllerValidation(config) { + + var controllers = config.controllers; + var listController = controllers.get('itemsController'); + var editController = controllers.get('editController'); + var listControllerPath = '%@.itemsController'.fmt(config.controllersPath); + var recordType = config.recordType; + + // Verify that instances at the first level of the tree are TreeItem instances + var items = listController.get('content'); + assertNonZeroLength(items, '%@.content'.fmt(listControllerPath)); + assertKindForList(recordType, items[0], + '%@.content[0]'.fmt(listControllerPath)); +} + +// Clone testing + +function treeControllerValidation(config) { + + var controllers = config.controllers; + var treeController = controllers.get('itemsController'); + var editController = controllers.get('editController'); + var treeControllerPath = '%@.itemsController'.fmt(config.controllersPath); + var recordType = config.recordType; + + // Verify that instances at the first level of the tree are TreeItem instances + var treeItems = treeController.get('treeItemChildren'); + assertNonZeroLength(treeItems, '%@.arrangedObjects'.fmt(treeControllerPath)); + assertKindForList(Footprint.TreeItem, treeItems, '%@.arrangedObjects'.fmt(treeControllerPath)); + // Verify that instances at the second level of the tree + assertNonZeroLength(treeItems[0].get('treeItemChildren'), + '%@.treeItemChildren[0].treeItemChildren'.fmt(treeControllerPath)); + assertKindForList(recordType, treeItems[0].get('treeItemChildren'), + '%@.treeItemChildren[0].treeItemChildren'.fmt(treeControllerPath)); +} + +// Clone testing +function cloneRecordTest(config) { + // Create a new record by cloning the editController.objectController.content + var editController = config.controllers.get('editController'); + // We shouldn't haven any content to edit yet. + assertNull(!editController.get('content')); + // Return this so we can monitor its _status property to become Footprint.Status.READY_NEW_CLONED + // createNew returns the controller content. + return editController.createNew() +} +// Call when the return of clone is READY_NEW +function cloneRecordValidation(config) { + var editController = config.controllers.get('editController'); + var controllerPath = config.controllersPath; + var editControllerPath = '%@.editController.content'.fmt(controllerPath); + assertNotNull(editController.get('content'), editControllerPath); + assertStatus(editController.get('content'), SC.Record.READY_NEW, editControllerPath); +} + +function saveClonedRecordTest(config) { + // Create a new record by cloning the editController.objectController.content + var editController = config.controllers.get('editController'); + editController.save(); + // Return the controller which delegates its content to the status, so the validation can wait for it to be READY_CLEAN + return editController; +} + +function saveClonedRecordValidation(config) { + var editController = config.controllers.get('editController'); + var controllerPath = config.controllersPath; + var editControllerPath = '%@.editController.content'.fmt(controllerPath); + assertStatus(editController, SC.Record.READY_CLEAN, editControllerPath); + // Make sure the record has saved to the server an has an id now + var savedContent = editController.get('content'); + assertNotNull(savedContent.get('id'), '%@.editController.content.id'.fmt(config.controllersPath)); + // Make sure the objectController was updated to the same record + var content = editController.getPath('objectController.content'); + var editControllerContentPath = '%@.editController.content'.fmt(controllerPath); + ok(savedContent===content, "%@ was saved but it is not reflected in the objectController.content property".fmt(editControllerContentPath)) +} + +function startUpdatePropertyTest(config) { + var editController = config.controllers.get('editController'); + // Reset the controller to begin the editing session + editController.updateCurrent(); + return editController; +} + +function updatePropertyTest(config, property, valueType) { + var editController = config.controllers.get('editController'); + var content = editController.get('content'); + content.addObserver('status', Footprint.tester, 'obs'); + assertNotNull(content, 'editController.content'); + + var oldValue = content.get(property); + var newValue = _newPropertyValue(content, property, valueType); + content.set(property, newValue); + // Make sure the property is not updated in the main store + var loadedContent = Footprint.store.find(editController.get('recordType'), content.get('id')); + equals(loadedContent.get(property), oldValue, "Expected content loaded from the main store to still have the original value"); + // Save the change + editController.save(); + // Return the content so it can be monitored for a READY_CLEAN status + return content; +} + +function updatePropertyValidation(config, property, valueType) { + var editController = config.controllers.get('editController'); + var content = editController.get('content'); + // Make sure the property is now updated in the main store + var reloadedContent = Footprint.store.find(editController.get('recordType'), content.get('id')); + var newValue = _newPropertyValue(content, property, valueType); + equals(reloadedContent.get(property), newValue, "Expected content reloaded from the main store to have the new value"); +} + +function _newPropertyValue(content, property, valueType) { + if (valueType==String) + return "%@_update".fmt(content.get(property)); + else + throw "No value generator for valueType %@".fmt(valueType); +} + diff --git a/sproutcore/frameworks/footprint/resources/integration_utils_for_views.js b/sproutcore/frameworks/footprint/resources/integration_utils_for_views.js new file mode 100644 index 000000000..250700521 --- /dev/null +++ b/sproutcore/frameworks/footprint/resources/integration_utils_for_views.js @@ -0,0 +1,343 @@ +/* + * UrbanFootprint-California (v1.0), Land Use Scenario Development and Modeling System. + * + * Copyright (C) 2012 Calthorpe Associates + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +/*** + * Utility functions for integration testing + */ + +/*** + * Setup a view for integration testings + * @param config. An object with configuration atrributes: + * attribute contentSetup: a no-argument function that initializes any content data and controllers to bind to the views. + * attribute views: a function that returns an array of one or more views to test. These views are simply added to the SC.MainPane that is created in this function and appended to the div of the DOM holding the unit test results. The pane css position is changed from absolute to relative so that the view doesn't cover up to test results. You should thus override the view's position left and top to 0 for testing. + * attribute datasource: an optional class to use for the Datasource, such as Footprint.Datasource. Defaults to SC.Record.fixtures + * attribute mainPane: optionally supply a mainpane instead of SC.MainPane. This is useful for full application view testing. This would probably be in place of the views options. + * @return {*} the SC.MainPane pane instance. Access the views with pane.childViews[] + */ +function viewSetup(config) { + + setupForTesting(config); + + SC.RunLoop.begin(); + + // Place the views to test in the MainPane + // Set the global Footprint.testPane to aid in debugging views. + var pane = Footprint.testPane = (config.mainPane || SC.MainPane).create(config.views ? { + childViews: config.views() + } : {}); + + // Set the login and user data so calls to the Footprint.DataSource have an apiKey + setUserContent(); + + Footprint.testStatechart = Footprint.Statechart.create({ + trace: YES, // Trace events that aren't handled + initialState: 'loadingApp' + }); + Footprint.testStatechart.initStatechart(); + pane.set('defaultResponder', Footprint.testStatechart); + + pane.appendTo($('.core-test')); // this would be '.jasmine_report' for jasmine tests + $('.core-test').css('height', '1000px'); + pane.$().css('position', 'relative'); + $('.passed').css('display', 'block'); + + config.contentSetup(); + + try { + SC.RunLoop.end(); + } catch(e) { + logError(e); + throw e; + } + + return pane; +} + +function viewTeardown() { + + $('.passed').css('display', 'block'); + // This could be turned on when runnning multiple tests to clear the pane + //SC.RunLoop.begin(); + //pane.remove(); + //pane = view = null; + //Footprint.store.reset(); + //SC.RunLoop.end(); +} + +/** + * Validates a contentView bound to a TreeController which uses a TreeContent for mixin + * @param pane: The SC.Pane instance + * @param data: 'treeController': The controller with the content data of the tree controller. + * 'treeControllerPath': A label to identify the tree controller. + * 'contentView': The content view bound to the TreeController. + * 'contentViewPath': A label for the contentView + * 'topLevelValidator': An optional function to validate each top level item, the function receives three args: The overall index of the tree item, the item view for the item, and the bound content item. + * 'bottomLevelValidator': Like topLevelValidator but for the bottom-level (inner) items. + */ +function treeViewValidation(pane, config) { + if (!config.controllers.get('itemsController')) + throw "itemsController is undefined" + return Footprint.Test.create({ + label:'View Test', + // start when this controller is READY_CLEAN + dependsOn: config.controllers.get('itemsController'), + // The dependsOn controller must be ready within this duration + onReadyTimeout:10000, + // The dependent test(s) must all finish within this duration + dependentTimeout:10000, + + dependents: [ + Footprint.Test.extend({ + label:'treeControllerValidation', + run: function() { + _treeViewValidation(pane, config); + } + }), + Footprint.Test.extend({ + label:'treeControllerValidation', + run: function() { + _treeViewSelectionValidation(pane, config); + } + }), + // Simulate a clone button click to create an edit pane that clones the selected item + Footprint.Test.extend({ + label:'cloneValidationSetup', + // The content status delegated by the edit controller should be READY_NEW + completeStatus: SC.Record.READY_NEW, + run: function() { + var panel = _cloneValidationSetup(pane, config); + return panel.get('editController') + } + }), + Footprint.Test.extend({ + label:'cloneValidation', + run: function() { + _cloneValidation(pane, config); + } + }) + ], + onDependentsComplete: function() { + throw "die so we can interact"; + start(); + } + + }); +} + +function _treeViewValidation(pane, config) { + var scGroupItemSelector = '.sc-group-item', + scListItemSelector = '.sc-list-item-view'; + + var treeContent = config.controllers.get('itemsController').get('content'); + var treeControllerPath = config.controllers.get('controllersPath')+ '.itemsController'; + var treeContentPath = treeControllerPath+'.content'; + + assertNotNull(config.controllers.get('itemsController'), treeControllerPath); + assertNotNull(treeContent, treeContentPath); + assertNotNull(config.contentView, config.contentViewPath); + + // Assert that there are as many top-level view nodes as there are treeItemChildren in the controller + assertNonZeroLength(treeContent.get('treeItemChildren'), treeContentPath+'.treeItemChildren'); + equals(pane.$(scGroupItemSelector).length, + treeContent.get('treeItemChildren').length, + 'Expect number matching tree item children count'); + // Make sure the arranged objects length matches our node length + keys length + // TODO this assumes no items with multiple keys + equals(config.controllers.get('itemsController').get('arrangedObjects').toArray().length, + config.controllers.get('itemsController').get('nodes').toArray().length + + config.controllers.get('itemsController').get('keyObjects').toArray().length, + 'itemsController.arrangedObjects'); + + // Assert that top-level node node has the expected number of child nodes. The nodes are actually siblings in the tree view DOM, so use nextUntil to get all up to the next tag node or the end of the siblings. + var actualTopLevelCounts = pane.$(scGroupItemSelector).map(function(i, selector) { + return $(selector).nextUntil(scGroupItemSelector).length; + }); + assertNotNull(treeContent.get('tree'), "Expecting the content object to mix in Footprint.TreeContent and implement its tree property for testing purposes"); + var expectedLowerLevelCounts = $.map(treeContent.get('tree'), function(dict, keyString) { + // make sure the tree data has no non-zero sub node values + assertNonZeroLength(dict.values, 'treeContent.tree.%@'.fmt(keyString)); + return dict.values.length; + }); + assertEqualLength(expectedLowerLevelCounts.length, actualTopLevelCounts.length, treeContent.tree); + + if (expectedLowerLevelCounts.length==actualTopLevelCounts.length) { + $.dualMap(expectedLowerLevelCounts, actualTopLevelCounts, function(expected, actual, index) { + equals(expected, actual, 'Expected %@ lower level items, found %@ for index %@'.fmt(expected, actual, index)); + }); + } + + // Flatten the first and second-level items and summon the corresponding view by index + // Run optional validators on the top-level and bottom-level items + $.each( + config.controllers.get('itemsController').get('arrangedObjects').toArray(), + function(i, contentItem) { + var itemView = config.contentView.itemViewForContentIndex(i); + if (itemView.$(scGroupItemSelector).length > 0) { + if (config.topLevelValidator) { + config.topLevelValidator(i, itemView, contentItem) + } + } + else { + // For SC.ListItemView there is no separate view for the label + //updateNameAndValidate(pane, itemView, contentItem, i); + if (config.bottomLevelValidator) { + config.bottomLevelValidator(i, itemView, contentItem) + } + } + }); +} + +/*** + * Select items in the list to make sure our activeObjectController's content updates + * @param pane + * @param config + * @private + */ +function _treeViewSelectionValidation(pane, config) { + var arrangedObjects = config.controllers.get('itemsController').get('arrangedObjects').toArray() + var recordType = config.controllers.getPath('editController.recordType'); + var contentItem = arrangedObjects.slice(-1)[0]; + var itemView = config.contentView.itemViewForContentIndex(arrangedObjects.indexOf(contentItem)); + assertNotNull(itemView, "Footprint.panel.contentView.(last item view)"); + // Click the last item and cycle the a run loop + mouseClick(itemView.$().get(0)); + // Assert that the selection updated + equals(config.controllers.getPath('itemsController.selection.firstObject'), contentItem, 'itemsController.selection.firstObject'); + // Assert that the objectController content updated + equals(config.controllers.getPath('editController.objectController.content'), contentItem, 'editController.objectController.content'); +} + +function _cloneValidationSetup(pane, config) { + var addButtonView = config.editbarView.addButtonView; + Footprint.testStatechart.firstCurrentState().add(addButtonView); + // It seems like a couple run loops need to run to complete all the bindings of the newly created EditPane + for (i=0; i<2; i++) { + SC.RunLoop.begin(); + SC.RunLoop.end(); + } + // Return the debug global panel variable + return Footprint.panel; +} + +function _cloneValidation(pane, config) { + var panel = Footprint.panel; + // Assert that all of the properties modeled for read or edit are set + var viewConfiguration = Footprint.InfoViewConfigurations[panel.get('recordType').toString()]; + viewConfiguration.get('attributes').forEach(function(key) { + var config = viewConfiguration.get(key); + // The EditItemView for the property + var editItemViewName = '%@View'.fmt(key); + var editItemView = panel.contentView[editItemViewName]; + assertNotNull(editItemView, "Footprint.panel.contentView.%@".fmt(editItemViewName)); + + // The expected display title of the view item + var title = config['title'] || SC.String.titleize(key); + equals(editItemView.get('title'), title); + + // The configured class of the item view + var itemViewClass = config['itemViewClass'] || Footprint.EditableModelStringView; + var itemView = editItemView.get('itemView'); + ok(itemView.kindOf(itemViewClass), "Expected the property %@ itemViewClass %@ to be a kindOf of %@".fmt( + "Footprint.panel.contentView.%@".fmt(editItemViewName), + itemView, + itemViewClass + )); + + // The properties of the itemView and their expected values relative to the content + var itemViewProperties = config['properties'] || SC.Object.create({attributes:['value'], value:'editController.%@'.fmt(key)}); + itemViewProperties.get('attributes').forEach(function(propertyName) { + var propertyPath = itemViewProperties[propertyName]; + // We expect the value in the editItemView to be that of the content + equals(itemView.getPath(propertyName), panel.getPath(propertyPath), "Footprint.panel.contentView.%@.itemView.%@".fmt(editItemViewName, propertyName)); + }); + }, this); +} + +/** + * Validates a ListView bound to a ListController + * @param pane: The SC.Pane instance + * @param data: 'listController': The controller with enumerable content data of the ListController. + * 'listControllerPath': A label to identify the listController. + * 'contentView': The ListView bound to the ListController. + * 'contentViewPath': A label for the contentView + * 'itemValidator': An optional function to validate each item, the function receives three args: The index of the tem, the item view for the item, and the bound content item. + */ +function listViewValidation(pane, data) { + + var scListItemSelector = '.sc-list-item-view'; + + var listContent = data.listController.get('content'); + var listContentPath = data.listControllerPath + '.content' + + assertNotNull(data.listController, data.listControllerPath); + assertNotNull(listContent, listContentPath); + assertNotNull(data.contentView, data.contentViewPath); + + // Assert that there are as many nodes as there are content itmes + assertNonZeroLength(listContent, listContentPath); + equals(pane.$(scListItemSelector).length, + listContent.toArray().length, + 'Expect number matching list item children count'); + + // Run optional validators on the view items + $.each( + listContent.toArray(), + function(i, contentItem) { + var itemView = data.contentView.itemViewForContentIndex(i); + if (data.itemValidator) { + data.itemValidator(i, itemView, contentItem) + } + }); +} + +/** + * Validates a SelectView bound to a ListController + * @param pane: The SC.Pane instance + * @param data: 'listController': The controller with enumerable content data of the ListController. + * 'listControllerPath': A label to identify the listController. + * 'itemActiveController': The controller with the active item of the ListController. + * 'itemActiveControllerPath': The path of the itemActiveController + * 'contentView': The SelectView bound to the ListController. + * 'contentViewPath': A label for the contentView + */ +function selectViewValidation(pane, data) { + + var listContent = data.listController.get('content'); + var listContentPath = data.listControllerPath + '.content' + + assertNotNull(data.listController, data.listControllerPath); + assertNotNull(listContent, listContentPath); + assertNotNull(data.contentView, data.contentViewPath); + + // Assert that there are as many nodes as there are content itmes + assertNonZeroLength(listContent, listContentPath); + // Since the selectView doesn't actually have a DOM element per item, we just check this obvious equality + equals(data.contentView.get('items').length(), + listContent.toArray().length, + 'Expect number matching'); + + var itemActiveContent = data.itemActiveController.get('content'); + ok(data.listController.contains(itemActiveContent), "Expect active content to be an item of the list controller"); + var activeIndex = data.listController.indexOf(itemActiveContent); + // Select the next item by simply changing the selected value. This should update the itemActiveController content via the two-binding + // TODO I'm not sure how to fire a select event via a mouse click--maybe if the SelectView has an exampleView there will be an actual DOM element to target. + var nextIndex = (activeIndex+1) % data.listController.length(); + data.contentView.set('value', data.listController.toArray()[nextIndex]); + // Update the bindings + SC.RunLoop.begin(); + SC.RunLoop.end(); + var updatedItemActiveContent = data.itemActiveController.get('content'); + ok(updatedItemActiveContent != itemActiveContent, "Expect the %@ to have an updated item".fmt(data.itemActiveControllerPath)); + equals(updatedItemActiveContent, data.listController.toArray()[nextIndex], 'Expect active item to equal the item at index %@ of the list controller'.fmt(nextIndex)); +} diff --git a/sproutcore/frameworks/footprint/resources/javascript_util.js b/sproutcore/frameworks/footprint/resources/javascript_util.js new file mode 100644 index 000000000..ef0e669ee --- /dev/null +++ b/sproutcore/frameworks/footprint/resources/javascript_util.js @@ -0,0 +1,27 @@ +/* + javascript.util is a port of selected parts of java.util to JavaScript which + main purpose is to ease porting Java code to JavaScript. + + The MIT License (MIT) + + Copyright (C) 2011,2012 by The Authors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +(function(){var e=function(t,n){var r=e.resolve(t,n||"/"),i=e.modules[r];if(!i)throw new Error("Failed to resolve module "+t+", tried "+r);var s=e.cache[r],o=s?s.exports:i();return o};e.paths=[],e.modules={},e.cache={},e.extensions=[".js",".coffee",".json"],e._core={assert:!0,events:!0,fs:!0,path:!0,vm:!0},e.resolve=function(){return function(t,n){function u(t){t=r.normalize(t);if(e.modules[t])return t;for(var n=0;n=0;i--){if(t[i]==="node_modules")continue;var s=t.slice(0,i+1).join("/")+"/node_modules";n.push(s)}return n}n||(n="/");if(e._core[t])return t;var r=e.modules.path();n=r.resolve("/",n);var i=n||"/";if(t.match(/^(?:\.\.?\/|\/)/)){var s=u(r.resolve(i,t))||a(r.resolve(i,t));if(s)return s}var o=f(t,i);if(o)return o;throw new Error("Cannot find module '"+t+"'")}}(),e.alias=function(t,n){var r=e.modules.path(),i=null;try{i=e.resolve(t+"/package.json","/")}catch(s){i=e.resolve(t,"/")}var o=r.dirname(i),u=(Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t})(e.modules);for(var a=0;a=0;r--){var i=e[r];i=="."?e.splice(r,1):i===".."?(e.splice(r,1),n++):n&&(e.splice(r,1),n--)}if(t)for(;n--;n)e.unshift("..");return e}var f=/^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;n.resolve=function(){var e="",t=!1;for(var n=arguments.length;n>=-1&&!t;n--){var r=n>=0?arguments[n]:s.cwd();if(typeof r!="string"||!r)continue;e=r+"/"+e,t=r.charAt(0)==="/"}return e=a(u(e.split("/"),function(e){return!!e}),!t).join("/"),(t?"/":"")+e||"."},n.normalize=function(e){var t=e.charAt(0)==="/",n=e.slice(-1)==="/";return e=a(u(e.split("/"),function(e){return!!e}),!t).join("/"),!e&&!t&&(e="."),e&&n&&(e+="/"),(t?"/":"")+e},n.join=function(){var e=Array.prototype.slice.call(arguments,0);return n.normalize(u(e,function(e,t){return e&&typeof e=="string"}).join("/"))},n.dirname=function(e){var t=f.exec(e)[1]||"",n=!1;return t?t.length===1||n&&t.length<=3&&t.charAt(1)===":"?t:t.substring(0,t.length-1):"."},n.basename=function(e,t){var n=f.exec(e)[2]||"";return t&&n.substr(-1*t.length)===t&&(n=n.substr(0,n.length-t.length)),n},n.extname=function(e){return f.exec(e)[3]||""}}),e.define("__browserify_process",function(e,t,n,r,i,s,o){var s=t.exports={};s.nextTick=function(){var e=typeof window!="undefined"&&window.setImmediate,t=typeof window!="undefined"&&window.postMessage&&window.addEventListener;if(e)return window.setImmediate;if(t){var n=[];return window.addEventListener("message",function(e){if(e.source===window&&e.data==="browserify-tick"){e.stopPropagation();if(n.length>0){var t=n.shift();t()}}},!0),function(t){n.push(t),window.postMessage("browserify-tick","*")}}return function(t){setTimeout(t,0)}}(),s.title="browser",s.browser=!0,s.env={},s.argv=[],s.binding=function(t){if(t==="evals")return e("vm");throw new Error("No such module. (Possibly not yet loaded)")},function(){var t="/",n;s.cwd=function(){return t},s.chdir=function(r){n||(n=e("path")),t=n.resolve(r,t)}}()}),e.define("/ArrayList.js",function(e,t,n,r,i,s,o){function h(){this.array=[],arguments[0]instanceof u&&this.addAll(arguments[0])}var u=e("./Collection"),a=e("./List"),f=e("./IndexOutOfBoundsException"),l=e("./NoSuchElementException"),c=e("./OperationNotSupported");h.prototype=new a,h.prototype.array=null,h.prototype.add=function(e){return this.array.push(e),!0},h.prototype.addAll=function(e){for(var t=e.iterator();t.hasNext();)this.add(t.next());return!0},h.prototype.set=function(e,t){var n=this.array[e];return this.array[e]=t,n},h.prototype.iterator=function(){return new h.Iterator(this)},h.prototype.get=function(e){if(e<0||e>=this.size())throw new f;return this.array[e]},h.prototype.isEmpty=function(){return this.array.length===0},h.prototype.size=function(){return this.array.length},h.prototype.toArray=function(){var e=[];for(var t=0,n=this.array.length;t. +* +* Contact: Joe DiStefano (joed@calthorpe.com), Calthorpe Associates. Firm contact: 2095 Rose Street Suite 201, Berkeley CA 94709. Phone: (510) 548-6800. Web: www.calthorpe.com + */ + +/** + * A Library of functional extensions to jquery to be created as needed. + * Functions and methods can be created as follows: + * + * $.fn.extend({ + * myMethod: function(){...} + * }); + * //jQuery("div").myMethod(); + * + * $.extend({ + * myFunction: function(){...} + * }); + * //jQuery.myFunction(); + */ + +$.extend({ + /** + * Returns true if any item passed to fun returns true. I can't believe this doesn't already exist in jQuery for non-selectors + * @param list + * @param func + * @return {*} + */ + any: function(list, func) { + var found = false; + $.each(list, function(index, value) { + // TODO this should pass i as the first arg to be consistent + if (func(value)) { + found = true; + return false; // break from loop + } + }); + return found; + }, + + /*** + * Returns true if all items pass the func filter + * @param list + * @param func + * @returns {boolean}jqueryE + */ + allMatch: function(list, func) { + return $.grep(list, func || function(x) { return x; }).length == list.length; + }, + + /*** + * Grep the obj to a new object + * @param obj + * @param func + * @returns {{}} + */ + grepObject: function(obj, func) { + var results = {}; + $.each(obj, function(key, value) { + if (func(key,value)) + results[key] = value; + }); + return results; + }, + + /*** + * extract just the values of an object into an array + * @param obj + */ + values: function(obj) { + var results = []; + $.each(obj, function(key, value) { + results.push(value); + }); + return results; + }, + + /** + * dualMap allows two equal length collections to be mapped in parallel + */ + dualMap: function(array1, array2, func) { + if (array1.length != array2.length) { + throw new Error($.format("Array lengths are not equal. array1:{0}, array2:{1}", + array1.length, + array2.length)); + } + return $.map(array1, function(value, index) { + return func(value, array2[index], index); + }); + }, + + /** + * dualMaps and returns true if all map to true + * @param array1 + * @param array2 + * @param func + * @returns {boolean} + */ + dualAllMatch: function(array1, array2, func) { + return $.allMatch($.dualMap(array1, array2, func)); + }, + + /** + * dualMapToDictionary maps two arrays to keys and values, respectively. The array must be the same length. + */ + dualMapToDictionary: function(array1, array2) { + if (array1.length != array2.length) { + throw new Error($.format("Array lengths are not equal. array1:{0}, array2:{1}", + array1.length, + array2.length)); + } + return $.mapToDictionary(array1, function(value, index) { + return [value, array2[index]]; + }); + }, + + /* Maps each value to a two item array which becomes a key value of a dictionary. Duplicates are overridden. The third optional argument is the objectType to construct. func takes a key values and should return a two item array, the transformed key value. If null is returned the key/value are not included + */ + mapToDictionary: function(array, func, objectType, target) { + func = func || function(a) { return a;} + var target = target || this; + var obj = objectType ? objectType() : {}; + $.map(array, function(value, index) { + var result = func.apply(target, [value, index]); + if (result) + obj[result[0]] = result[1] + }); + return obj; + }, + + /*** + * Maps each value to a two item array which becomes a key and value of a result Object. Duplicates are overridden. The third optional argument is the Object type to construct + * @param obj: The object to map + * @param func: A function that expects a key and value, which maps each pair from obj. Return null to exclude a pair from the results + * @param objectType: The optional objectType to use to construct the result object. Defaults to Object. This can also be a function that returns the constructed object + * @param keyRegex Filter through only keys matching the regex, /^[a-z]/ by default + * @return {*} An object with the mapped key/values + */ + mapObjectToObject: function(obj, func, objectType, keyRegex) { + func = func || function(a) { return a;} + var resultObj = objectType ? objectType() : {}; + $.each(obj, function(key, value) { + if (!keyRegex || key.match(keyRegex)) { + var result = func(key, value); + if (result) + resultObj[result[0]] = result[1]; + } + }); + return resultObj; + }, + + /** + * Maps the array to a function that returns a list of values where each value represents a dimension of the result hash, + * except for the last value which represents the value of the inner most hash. + * Thus three function returning ['a','b','c'], ['a','d','e'], ['b','f','g'] + * create a hash {a:{b:c}, a:{d:e}, b:{f:g}} + * Duplicates are overwritten + * @param array + * @param func + * @return {Object} + */ + mapToMultiDimensionalDictionary: function(array, func) { + var hash = {}; + $.each(array, function(index, value) { + results = func(value, index); + $.addDeep(hash, results) + }); + return hash; + }, + + /** + * Maps the given array to a dictionary keyed by the results of keyFunc and valued by the results of valueFunc. Duplicate key results merge their value results into a single array. Use this function when the keyFunc needs to return objects. Javascript doesn't support objects as keys, so you must supply a keyToString function that maps the result of keyFunc to a string to use for the actual key. The key object will be stored in the value, as described below. + * @param array - values to map + * @param keyFunc - operates on each array item and returns a single or array of keys. Each key value returned becomes a key of the result hash. The key is also added to the value of each hash entry at entry.key. The reason for this is that hash keys are always strings, so this maintains the obj value of the key + * @param valueFunc - operates on each array item and adds the value for the result hash entry.values attribute. The same value is applied to each result of keyFunc + * @param keyToString - optionally converts each key from keyFunc to a string for the actual keys of the result hash. It's important to have a simple key like an id so that the uniqueness check works on the keys + * @param objectType The object type to use for the result. {} by default. You can pass a function or class here, either will be invoked using objectType() + * Example args [1,2,3], function(x) { return [new Foo(bar:'a'),new Foo(bar:'b')] }, function (foo) { return foo.bar; } results in + * {'a':[{key:foo(with bar a), values:[1,2,3], 'b':{key:foo(with bar b), values:[1,2,3]}} + * @return {Object} + */ + mapToCollectionsObjectWithObjectKeys: function(array, keyFunc, valueFunc, keyToString, objectType) { + objectType = objectType || Object; + var resultObj = objectType ? objectType() : {}; + keyToString = keyToString || function(k) { return k;}; + array.forEach(function(value, index) { + key = keyFunc(value, index); + value = valueFunc(value, index); + arrayOrItemToArray(key).forEach(function(k, i) { + var keyString = keyToString(k); + if (!resultObj[keyString]) resultObj[keyString] = {key:k, values:[]}; + jQuery.merge(resultObj[keyString].values, [value]); + }); + }); + return resultObj; + }, + /** + * Likes mapToCollectionsObjectWithStrongKeys, but assumes the key is a primitive, and thus returns a simple Object of the mapped keys and value collections + * @param array The array or enumerable to map + * @param keyFunc Maps an array value to a key or array of keys. + * @param valueFunc Maps an array value to a value. values of the same key are concatinated into a single array + * @param objectType The object type to use for the result. {} by default. You can pass a function or class here, either will be invoked using objectType() + * @param keyRegex Filter through only keys matching the regex, /^[a-z]/ by default + * @returns {Object} An object of the given objectType keyed by the results of keyFunc and valued by the concatinated items of valueFunc + */ + mapToCollectionsObject: function(array, keyFunc, valueFunc, objectType, keyRegex) { + keyRegex = keyRegex || /^[a-z]/ + return jQuery.mapObjectToObject( + jQuery.mapToCollectionsObjectWithObjectKeys(array, keyFunc, valueFunc, null, objectType), + function(key, value) { + return [key, value.values]; + }, + objectType, + keyRegex); + }, + + /** + * Like map but sends the previous result to func as a second argument, and the current index as the third. + * This is a like an accumulator or reducer, but just intended for chaining values together + * Returns the mapped items. + * @param array + * @param func: Takes each item, the previous func result or null, and the current index + */ + mapWithPreviousResult: function(array, func) { + var results = [], + previous = null; + jQuery.each(array, function(i, item) { + previous = func(item, previous, i); + results.push(previous); + }); + return results; + }, + accumulate: function(array, func) { + return this.mapWithPreviousResult(array, func).slice(-1)[0]; + }, + + /*** + * Calls the value on the given function if not null. Otherwise returns undefined + * @param value + * @param func function that takes value as its argument + */ + ifNotNull: function(value, func) { + if (value) + return func(value); + }, + + /*** + * Returns an array or a default if not an array + * @param array + * @param defaultArray + * @returns {*} + */ + arrayOrIfEmpty: function(array, defaultArray) { + return Array.isArray(array) ? array : defaultArray; + }, + + arrayOrSingleItemArray: function(array) { + return Array.isArray(array) ? array : [array] + }, + +// ************************************************************************** +// Copyright 2007 - 2009 Tavs Dokkedahl +// Contact: http://www.jslab.dk/contact.php +// +// This file is part of the JSLab Standard Library (JSL) Program. +// +// JSL is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// any later version. +// +// JSL is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// *************************************************************************** + +// Return new array with duplicate values removed +// Modified to extend jQuery instead of Array + uniqueItems: function(array) { + return jQuery.uniqueBy(array) + }, + lastItem: function(array) { + return array[array.length-1]; + }, + /*** + * Filter by unique item by mapping each value to something else, like an id, that can be checked for uniqueness + * @param array + * @param uniqueMap + */ + uniqueBy: function(array, uniqueMap) { + uniqueMap = uniqueMap || function(x){return x;}; + var a = []; + var l = array.length; + for(var i=0; ihiPt.y){hiPt=p;hiIndex=i;}} +iPrev=hiIndex;do{iPrev=iPrev-1;if(iPrev<0){iPrev=nPts;}}while(ring[iPrev].equals2D(hiPt)&&iPrev!==hiIndex);iNext=hiIndex;do{iNext=(iNext+1)%nPts;}while(ring[iNext].equals2D(hiPt)&&iNext!==hiIndex);prev=ring[iPrev];next=ring[iNext];if(prev.equals2D(hiPt)||next.equals2D(hiPt)||prev.equals2D(next)){return false;} +disc=jsts.algorithm.CGAlgorithms.computeOrientation(prev,hiPt,next);isCCW=false;if(disc===0){isCCW=(prev.x>next.x);}else{isCCW=(disc>0);} +return isCCW;};jsts.algorithm.CGAlgorithms.computeOrientation=function(p1,p2,q){return jsts.algorithm.CGAlgorithms.orientationIndex(p1,p2,q);};jsts.algorithm.CGAlgorithms.distancePointLine=function(p,A,B){if(!(A instanceof jsts.geom.Coordinate)){jsts.algorithm.CGAlgorithms.distancePointLine2.apply(this,arguments);} +if(A.x===B.x&&A.y===B.y){return p.distance(A);} +var r,s;r=((p.x-A.x)*(B.x-A.x)+(p.y-A.y)*(B.y-A.y))/((B.x-A.x)*(B.x-A.x)+(B.y-A.y)*(B.y-A.y));if(r<=0.0){return p.distance(A);} +if(r>=1.0){return p.distance(B);} +s=((A.y-p.y)*(B.x-A.x)-(A.x-p.x)*(B.y-A.y))/((B.x-A.x)*(B.x-A.x)+(B.y-A.y)*(B.y-A.y));return Math.abs(s)*Math.sqrt(((B.x-A.x)*(B.x-A.x)+(B.y-A.y)*(B.y-A.y)));};jsts.algorithm.CGAlgorithms.distancePointLinePerpendicular=function(p,A,B){var s=((A.y-p.y)*(B.x-A.x)-(A.x-p.x)*(B.y-A.y))/((B.x-A.x)*(B.x-A.x)+(B.y-A.y)*(B.y-A.y));return Math.abs(s)*Math.sqrt(((B.x-A.x)*(B.x-A.x)+(B.y-A.y)*(B.y-A.y)));};jsts.algorithm.CGAlgorithms.distancePointLine2=function(p,line){var minDistance,i,il,dist;if(line.length===0){throw new jsts.error.IllegalArgumentError('Line array must contain at least one vertex');} +minDistance=p.distance(line[0]);for(i=0,il=line.length-1;i1)||(s<0)||(s>1)){return Math.min(jsts.algorithm.CGAlgorithms.distancePointLine(A,C,D),Math.min(jsts.algorithm.CGAlgorithms.distancePointLine(B,C,D),Math.min(jsts.algorithm.CGAlgorithms.distancePointLine(C,A,B),jsts.algorithm.CGAlgorithms.distancePointLine(D,A,B))));} +return 0.0;};jsts.algorithm.CGAlgorithms.signedArea=function(ring){if(ring.length<3){return 0.0;} +var sum,i,il,bx,by,cx,cy;sum=0.0;for(i=0,il=ring.length-1;i0;};jsts.algorithm.Angle.isObtuse=function(p0,p1,p2){var dx0,dy0,dx1,dy1,dotprod;dx0=p0.x-p1.x;dy0=p0.y-p1.y;dx1=p2.x-p1.x;dy1=p2.y-p1.y;dotprod=dx0*dx1+dy0*dy1;return dotprod<0;};jsts.algorithm.Angle.angleBetween=function(tip1,tail,tip2){var a1,a2;a1=jsts.algorithm.Angle.angle(tail,tip1);a2=jsts.algorithm.Angle.angle(tail,tip2);return jsts.algorithm.Angle.diff(a1,a2);};jsts.algorithm.Angle.angleBetweenOriented=function(tip1,tail,tip2){var a1,a2,angDel;a1=jsts.algorithm.Angle.angle(tail,tip1);a2=jsts.algorithm.Angle.angle(tail,tip2);angDel=a2-a1;if(angDel<=-Math.PI){return angDel+jsts.algorithm.Angle.PI_TIMES_2;} +if(angDel>Math.PI){return angDel-jsts.algorithm.Angle.PI_TIMES_2;} +return angDel;};jsts.algorithm.Angle.interiorAngle=function(p0,p1,p2){var anglePrev,angleNext;anglePrev=jsts.algorithm.Angle.angle(p1,p0);angleNext=jsts.algorithm.Angle.angle(p1,p2);return Math.abs(angleNext-anglePrev);};jsts.algorithm.Angle.getTurn=function(ang1,ang2){var crossproduct=Math.sin(ang2-ang1);if(crossproduct>0){return jsts.algorithm.Angle.COUNTERCLOCKWISE;} +if(crossproduct<0){return jsts.algorithm.Angle.CLOCKWISE;} +return jsts.algorithm.Angle.NONE;};jsts.algorithm.Angle.normalize=function(angle){while(angle>Math.PI){angle-=jsts.algorithm.Angle.PI_TIMES_2;} +while(angle<=-Math.PI){angle+=jsts.algorithm.Angle.PI_TIMES_2;} +return angle;};jsts.algorithm.Angle.normalizePositive=function(angle){if(angle<0.0){while(angle<0.0){angle+=jsts.algorithm.Angle.PI_TIMES_2;} +if(angle>=jsts.algorithm.Angle.PI_TIMES_2){angle=0.0;}} +else{while(angle>=jsts.algorithm.Angle.PI_TIMES_2){angle-=jsts.algorithm.Angle.PI_TIMES_2;} +if(angle<0.0){angle=0.0;}} +return angle;};jsts.algorithm.Angle.diff=function(ang1,ang2){var delAngle;if(ang1Math.PI){delAngle=(2*Math.PI)-delAngle;} +return delAngle;};jsts.geom.GeometryComponentFilter=function(){};jsts.geom.GeometryComponentFilter.prototype.filter=function(geom){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.util.LinearComponentExtracter=function(lines,isForcedToLineString){this.lines=lines;this.isForcedToLineString=isForcedToLineString;};jsts.geom.util.LinearComponentExtracter.prototype=new jsts.geom.GeometryComponentFilter();jsts.geom.util.LinearComponentExtracter.prototype.lines=null;jsts.geom.util.LinearComponentExtracter.prototype.isForcedToLineString=false;jsts.geom.util.LinearComponentExtracter.getLines=function(geoms,lines){if(arguments.length==1){return jsts.geom.util.LinearComponentExtracter.getLines5.apply(this,arguments);} +else if(arguments.length==2&&typeof lines==='boolean'){return jsts.geom.util.LinearComponentExtracter.getLines6.apply(this,arguments);} +else if(arguments.length==2&&geoms instanceof jsts.geom.Geometry){return jsts.geom.util.LinearComponentExtracter.getLines3.apply(this,arguments);} +else if(arguments.length==3&&geoms instanceof jsts.geom.Geometry){return jsts.geom.util.LinearComponentExtracter.getLines4.apply(this,arguments);} +else if(arguments.length==3){return jsts.geom.util.LinearComponentExtracter.getLines2.apply(this,arguments);} +for(var i=0;idistance){return false;} +return DistanceOp.isWithinDistance(this,geom,distance);};jsts.geom.Geometry.prototype.isRectangle=function(){return false;};jsts.geom.Geometry.prototype.getArea=function(){return 0.0;};jsts.geom.Geometry.prototype.getLength=function(){return 0.0;};jsts.geom.Geometry.prototype.getCentroid=function(){if(this.isEmpty()){return null;} +var cent;var centPt=null;var dim=this.getDimension();if(dim===0){cent=new jsts.algorithm.CentroidPoint();cent.add(this);centPt=cent.getCentroid();}else if(dim===1){cent=new jsts.algorithm.CentroidLine();cent.add(this);centPt=cent.getCentroid();}else{cent=new jsts.algorithm.CentroidArea();cent.add(this);centPt=cent.getCentroid();} +return this.createPointFromInternalCoord(centPt,this);};jsts.geom.Geometry.prototype.getInteriorPoint=function(){var intPt;var interiorPt=null;var dim=this.getDimension();if(dim===0){intPt=new InteriorPointPoint(this);interiorPt=intPt.getInteriorPoint();}else if(dim===1){intPt=new InteriorPointLine(this);interiorPt=intPt.getInteriorPoint();}else{intPt=new InteriorPointArea(this);interiorPt=intPt.getInteriorPoint();} +return this.createPointFromInternalCoord(interiorPt,this);};jsts.geom.Geometry.prototype.getDimension=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.Geometry.prototype.getBoundary=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.Geometry.prototype.getBoundaryDimension=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.Geometry.prototype.getEnvelope=function(){return this.getFactory().toGeometry(this.getEnvelopeInternal());};jsts.geom.Geometry.prototype.getEnvelopeInternal=function(){if(this.envelope===null){this.envelope=this.computeEnvelopeInternal();} +return this.envelope;};jsts.geom.Geometry.prototype.disjoint=function(g){return!this.intersects(g);};jsts.geom.Geometry.prototype.touches=function(g){if(!this.getEnvelopeInternal().intersects(g.getEnvelopeInternal())){return false;} +return this.relate(g).isTouches(this.getDimension(),g.getDimension());};jsts.geom.Geometry.prototype.intersects=function(g){if(!this.getEnvelopeInternal().intersects(g.getEnvelopeInternal())){return false;} +if(this.isRectangle()){return RectangleIntersects.intersects(this,g);} +if(g.isRectangle()){return RectangleIntersects.intersects(g,this);} +return this.relate(g).isIntersects();};jsts.geom.Geometry.prototype.crosses=function(g){if(!this.getEnvelopeInternal().intersects(g.getEnvelopeInternal())){return false;} +return this.relate(g).isCrosses(this.getDimension(),g.getDimension());};jsts.geom.Geometry.prototype.within=function(g){return g.contains(this);};jsts.geom.Geometry.prototype.contains=function(g){if(!this.getEnvelopeInternal().contains(g.getEnvelopeInternal())){return false;} +if(this.isRectangle()){return RectangleContains.contains(this,g);} +return this.relate(g).isContains();};jsts.geom.Geometry.prototype.overlaps=function(g){if(!this.getEnvelopeInternal().intersects(g.getEnvelopeInternal())){return false;} +return this.relate(g).isOverlaps(this.getDimension(),g.getDimension());};jsts.geom.Geometry.prototype.covers=function(g){if(!this.getEnvelopeInternal().covers(g.getEnvelopeInternal())){return false;} +if(this.isRectangle()){return true;} +return this.relate(g).isCovers();};jsts.geom.Geometry.prototype.coveredBy=function(g){return g.covers(this);};jsts.geom.Geometry.prototype.relate=function(g,intersectionPattern){if(arguments.length===1){return this.relate2.apply(this,arguments);} +return this.relate2(g).matches(intersectionPattern);};jsts.geom.Geometry.prototype.relate2=function(g){this.checkNotGeometryCollection(this);this.checkNotGeometryCollection(g);return jsts.operation.relate.RelateOp.relate(this,g);};jsts.geom.Geometry.prototype.equalsTopo=function(g){if(!this.getEnvelopeInternal().equals(g.getEnvelopeInternal())){return false;} +return this.relate(g).isEquals(this.getDimension(),g.getDimension());};jsts.geom.Geometry.prototype.equals=function(o){if(o instanceof jsts.geom.Geometry||o instanceof jsts.geom.LinearRing||o instanceof jsts.geom.Polygon||o instanceof jsts.geom.GeometryCollection||o instanceof jsts.geom.MultiPoint||o instanceof jsts.geom.MultiLineString||o instanceof jsts.geom.MultiPolygon){return this.equalsExact(o);} +return false;};jsts.geom.Geometry.prototype.buffer=function(distance,quadrantSegments,endCapStyle){var params=new jsts.operation.buffer.BufferParameters(quadrantSegments,endCapStyle) +return jsts.operation.buffer.BufferOp.bufferOp2(this,distance,params);};jsts.geom.Geometry.prototype.convexHull=function(){return new jsts.algorithm.ConvexHull(this).getConvexHull();};jsts.geom.Geometry.prototype.intersection=function(other){if(this.isEmpty()){return this.getFactory().createGeometryCollection(null);} +if(other.isEmpty()){return this.getFactory().createGeometryCollection(null);} +if(this.isGeometryCollection(this)){var g2=other;} +this.checkNotGeometryCollection(this);this.checkNotGeometryCollection(other);return jsts.operation.overlay.snap.SnapIfNeededOverlayOp.overlayOp(this,other,jsts.operation.overlay.OverlayOp.INTERSECTION);};jsts.geom.Geometry.prototype.union=function(other){if(arguments.length===0){return jsts.operation.union.UnaryUnionOp.union(this);} +if(this.isEmpty()){return other.clone();} +if(other.isEmpty()){return this.clone();} +this.checkNotGeometryCollection(this);this.checkNotGeometryCollection(other);return jsts.operation.overlay.snap.SnapIfNeededOverlayOp.overlayOp(this,other,jsts.operation.overlay.OverlayOp.UNION);};jsts.geom.Geometry.prototype.difference=function(other){if(this.isEmpty()){return this.getFactory().createGeometryCollection(null);} +if(other.isEmpty()){return this.clone();} +this.checkNotGeometryCollection(this);this.checkNotGeometryCollection(other);return jsts.operation.overlay.snap.SnapIfNeededOverlayOp.overlayOp(this,other,jsts.operation.overlay.OverlayOp.DIFFERENCE);};jsts.geom.Geometry.prototype.symDifference=function(other){if(this.isEmpty()){return other.clone();} +if(other.isEmpty()){return this.clone();} +this.checkNotGeometryCollection(this);this.checkNotGeometryCollection(other);return jsts.operation.overlay.snap.SnapIfNeededOverlayOp.overlayOp(this,other,jsts.operation.overlay.OverlayOp.SYMDIFFERENCE);};jsts.geom.Geometry.prototype.equalsExact=function(other,tolerance){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.Geometry.prototype.equalsNorm=function(g){if(g===null||g===undefined) +return false;return this.norm().equalsExact(g.norm());};jsts.geom.Geometry.prototype.apply=function(filter){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.Geometry.prototype.clone=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.Geometry.prototype.normalize=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.Geometry.prototype.norm=function(){var copy=this.clone();copy.normalize();return copy;};jsts.geom.Geometry.prototype.compareTo=function(o){var other=o;if(this.getClassSortIndex()!==other.getClassSortIndex()){return this.getClassSortIndex()-other.getClassSortIndex();} +if(this.isEmpty()&&other.isEmpty()){return 0;} +if(this.isEmpty()){return-1;} +if(other.isEmpty()){return 1;} +return this.compareToSameClass(o);};jsts.geom.Geometry.prototype.isEquivalentClass=function(other){if(this instanceof jsts.geom.Point&&other instanceof jsts.geom.Point){return true;}else if(this instanceof jsts.geom.LineString&&(other instanceof jsts.geom.LineString|other instanceof jsts.geom.LinearRing)){return true;}else if(this instanceof jsts.geom.LinearRing&&(other instanceof jsts.geom.LineString|other instanceof jsts.geom.LinearRing)){return true;}else if(this instanceof jsts.geom.Polygon&&(other instanceof jsts.geom.Polygon)){return true;}else if(this instanceof jsts.geom.MultiPoint&&(other instanceof jsts.geom.MultiPoint)){return true;}else if(this instanceof jsts.geom.MultiLineString&&(other instanceof jsts.geom.MultiLineString)){return true;}else if(this instanceof jsts.geom.MultiPolygon&&(other instanceof jsts.geom.MultiPolygon)){return true;}else if(this instanceof jsts.geom.GeometryCollection&&(other instanceof jsts.geom.GeometryCollection)){return true;} +return false;};jsts.geom.Geometry.prototype.checkNotGeometryCollection=function(g){if(g.isGeometryCollectionBase()){throw new jsts.error.IllegalArgumentError('This method does not support GeometryCollection');}};jsts.geom.Geometry.prototype.isGeometryCollection=function(){return(this instanceof jsts.geom.GeometryCollection);};jsts.geom.Geometry.prototype.isGeometryCollectionBase=function(){return(this.CLASS_NAME==='jsts.geom.GeometryCollection');};jsts.geom.Geometry.prototype.computeEnvelopeInternal=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.Geometry.prototype.compareToSameClass=function(o){throw new jsts.error.AbstractMethodInvocationError();};jsts.geom.Geometry.prototype.compare=function(a,b){var i=a.iterator();var j=b.iterator();while(i.hasNext()&&j.hasNext()){var aElement=i.next();var bElement=j.next();var comparison=aElement.compareTo(bElement);if(comparison!==0){return comparison;}} +if(i.hasNext()){return 1;} +if(j.hasNext()){return-1;} +return 0;};jsts.geom.Geometry.prototype.equal=function(a,b,tolerance){if(tolerance===undefined||tolerance===null||tolerance===0){return a.equals(b);} +return a.distance(b)<=tolerance;};jsts.geom.Geometry.prototype.getClassSortIndex=function(){var sortedClasses=[jsts.geom.Point,jsts.geom.MultiPoint,jsts.geom.LineString,jsts.geom.LinearRing,jsts.geom.MultiLineString,jsts.geom.Polygon,jsts.geom.MultiPolygon,jsts.geom.GeometryCollection];for(var i=0;iother.x){return 1;} +if(this.yother.y){return 1;} +return 0;};jsts.geom.Coordinate.prototype.toString=function(){return'('+this.x+', '+this.y+')';};})();jsts.geom.Envelope=function(){jsts.geom.Envelope.prototype.init.apply(this,arguments);};jsts.geom.Envelope.prototype.minx=null;jsts.geom.Envelope.prototype.maxx=null;jsts.geom.Envelope.prototype.miny=null;jsts.geom.Envelope.prototype.maxy=null;jsts.geom.Envelope.prototype.init=function(){if(typeof arguments[0]==='number'&&arguments.length===4){this.initFromValues(arguments[0],arguments[1],arguments[2],arguments[3]);}else if(arguments[0]instanceof jsts.geom.Coordinate&&arguments.length===1){this.initFromCoordinate(arguments[0]);}else if(arguments[0]instanceof jsts.geom.Coordinate&&arguments.length===2){this.initFromCoordinates(arguments[0],arguments[1]);}else if(arguments[0]instanceof jsts.geom.Envelope&&arguments.length===1){this.initFromEnvelope(arguments[0]);}else{this.setToNull();}};jsts.geom.Envelope.prototype.initFromValues=function(x1,x2,y1,y2){if(x1this.maxx){this.maxx=x;} +if(ythis.maxy){this.maxy=y;}}};jsts.geom.Envelope.prototype.expandToIncludeEnvelope=function(other){if(other.isNull()){return;} +if(this.isNull()){this.minx=other.getMinX();this.maxx=other.getMaxX();this.miny=other.getMinY();this.maxy=other.getMaxY();}else{if(other.minxthis.maxx){this.maxx=other.maxx;} +if(other.minythis.maxy){this.maxy=other.maxy;}}};jsts.geom.Envelope.prototype.expandBy=function(){if(arguments.length===1){this.expandByDistance(arguments[0]);}else{this.expandByDistances(arguments[0],arguments[1]);}};jsts.geom.Envelope.prototype.expandByDistance=function(distance){this.expandByDistances(distance,distance);};jsts.geom.Envelope.prototype.expandByDistances=function(deltaX,deltaY){if(this.isNull()){return;} +this.minx-=deltaX;this.maxx+=deltaX;this.miny-=deltaY;this.maxy+=deltaY;if(this.minx>this.maxx||this.miny>this.maxy){this.setToNull();}};jsts.geom.Envelope.prototype.translate=function(transX,transY){if(this.isNull()){return;} +this.init(this.minx+transX,this.maxx+transX,this.miny+transY,this.maxy+transY);};jsts.geom.Envelope.prototype.centre=function(){if(this.isNull()){return null;} +return new jsts.geom.Coordinate((this.minx+this.maxx)/2.0,(this.miny+this.maxy)/2.0);};jsts.geom.Envelope.prototype.intersection=function(env){if(this.isNull()||env.isNull()||!this.intersects(env)){return new jsts.geom.Envelope();} +var intMinX=this.minx>env.minx?this.minx:env.minx;var intMinY=this.miny>env.miny?this.miny:env.miny;var intMaxX=this.maxxthis.maxx||other.maxxthis.maxy||other.maxythis.maxx||xthis.maxy||y=this.minx&&x<=this.maxx&&y>=this.miny&&y<=this.maxy;};jsts.geom.Envelope.prototype.coversCoordinate=function(p){return this.coversValues(p.x,p.y);};jsts.geom.Envelope.prototype.coversEnvelope=function(other){if(this.isNull()||other.isNull()){return false;} +return other.minx>=this.minx&&other.maxx<=this.maxx&&other.miny>=this.miny&&other.maxy<=this.maxy;};jsts.geom.Envelope.prototype.distance=function(env){if(this.intersects(env)){return 0;} +var dx=0.0;if(this.maxxenv.maxx){dx=this.minx-env.maxx;} +var dy=0.0;if(this.maxyenv.maxy){dy=this.miny-env.maxy;} +if(dx===0.0){return dy;} +if(dy===0.0){return dx;} +return Math.sqrt(dx*dx+dy*dy);};jsts.geom.Envelope.prototype.equals=function(other){if(this.isNull()){return other.isNull();} +return this.maxx===other.maxx&&this.maxy===other.maxy&&this.minx===other.minx&&this.miny===other.miny;};jsts.geom.Envelope.prototype.toString=function(){return'Env['+this.minx+' : '+this.maxx+', '+this.miny+' : '+ +this.maxy+']';};jsts.geom.Envelope.intersects=function(p1,p2,q){if(arguments.length===4){return jsts.geom.Envelope.intersectsEnvelope(arguments[0],arguments[1],arguments[2],arguments[3]);} +var xc1=p1.xp2.x?p1.x:p2.x;var yc1=p1.yp2.y?p1.y:p2.y;if(((q.x>=xc1)&&(q.x<=xc2))&&((q.y>=yc1)&&(q.y<=yc2))){return true;} +return false;};jsts.geom.Envelope.intersectsEnvelope=function(p1,p2,q1,q2){var minq=Math.min(q1.x,q2.x);var maxq=Math.max(q1.x,q2.x);var minp=Math.min(p1.x,p2.x);var maxp=Math.max(p1.x,p2.x);if(minp>maxq){return false;} +if(maxpmaxq){return false;} +if(maxp1)return this.combine2.apply(this,arguments);var combiner=new jsts.geom.util.GeometryCombiner(geoms);return combiner.combine();};jsts.geom.util.GeometryCombiner.combine2=function(){var arrayList=new javascript.util.ArrayList();arguments.foreach(function(a){arrayList.add(a);}) +var combiner=jsts.geom.util.GeometryCombiner(arrayList);return combiner.combine();};jsts.geom.util.GeometryCombiner.prototype.geomFactory=null;jsts.geom.util.GeometryCombiner.prototype.skipEmpty=false;jsts.geom.util.GeometryCombiner.prototype.inputGeoms;jsts.geom.util.GeometryCombiner.extractFactory=function(geoms){if(geoms.isEmpty())return null;return geoms.iterator().next().getFactory();};jsts.geom.util.GeometryCombiner.prototype.combine=function(){var elems=new javascript.util.ArrayList(),i;for(i=this.inputGeoms.iterator();i.hasNext();){var g=i.next();this.extractElements(g,elems);} +if(elems.size()===0){if(this.geomFactory!==null){return geomFactory.createGeometryCollection(null);} +return null;} +return this.geomFactory.buildGeometry(elems);};jsts.geom.util.GeometryCombiner.prototype.extractElements=function(geom,elems){if(geom===null){return;} +for(var i=0;isegmentIndex) +return 1;if(this.distdist) +return 1;return 0;};jsts.geomgraph.EdgeIntersection.prototype.isEndPoint=function(maxSegmentIndex){if(this.segmentIndex===0&&this.dist===0.0) +return true;if(this.segmentIndex===maxSegmentIndex) +return true;return false;};jsts.geomgraph.EdgeIntersection.prototype.toString=function(){return''+this.segmentIndex+this.dist;};(function(){var EdgeIntersection=jsts.geomgraph.EdgeIntersection;var TreeMap=javascript.util.TreeMap;jsts.geomgraph.EdgeIntersectionList=function(edge){this.nodeMap=new TreeMap();this.edge=edge;};jsts.geomgraph.EdgeIntersectionList.prototype.nodeMap=null;jsts.geomgraph.EdgeIntersectionList.prototype.edge=null;jsts.geomgraph.EdgeIntersectionList.prototype.isIntersection=function(pt){for(var it=this.iterator();it.hasNext();){var ei=it.next();if(ei.coord.equals(pt)){return true;}} +return false;};jsts.geomgraph.EdgeIntersectionList.prototype.add=function(intPt,segmentIndex,dist){var eiNew=new EdgeIntersection(intPt,segmentIndex,dist);var ei=this.nodeMap.get(eiNew);if(ei!==null){return ei;} +this.nodeMap.put(eiNew,eiNew);return eiNew;};jsts.geomgraph.EdgeIntersectionList.prototype.iterator=function(){return this.nodeMap.values().iterator();};jsts.geomgraph.EdgeIntersectionList.prototype.addEndpoints=function(){var maxSegIndex=this.edge.pts.length-1;this.add(this.edge.pts[0],0,0.0);this.add(this.edge.pts[maxSegIndex],maxSegIndex,0.0);};jsts.geomgraph.EdgeIntersectionList.prototype.addSplitEdges=function(edgeList) +{this.addEndpoints();var it=this.iterator();var eiPrev=it.next();while(it.hasNext()){var ei=it.next();var newEdge=this.createSplitEdge(eiPrev,ei);edgeList.add(newEdge);eiPrev=ei;}};jsts.geomgraph.EdgeIntersectionList.prototype.createSplitEdge=function(ei0,ei1){var npts=ei1.segmentIndex-ei0.segmentIndex+2;var lastSegStartPt=this.edge.pts[ei1.segmentIndex];var useIntPt1=ei1.dist>0.0||!ei1.coord.equals2D(lastSegStartPt);if(!useIntPt1){npts--;} +var pts=[];var ipt=0;pts[ipt++]=new jsts.geom.Coordinate(ei0.coord);for(var i=ei0.segmentIndex+1;i<=ei1.segmentIndex;i++){pts[ipt++]=this.edge.pts[i];} +if(useIntPt1)pts[ipt]=ei1.coord;return new jsts.geomgraph.Edge(pts,new jsts.geomgraph.Label(this.edge.label));};})();jsts.geom.Location=function(){};jsts.geom.Location.INTERIOR=0;jsts.geom.Location.BOUNDARY=1;jsts.geom.Location.EXTERIOR=2;jsts.geom.Location.NONE=-1;jsts.geom.Location.toLocationSymbol=function(locationValue){switch(locationValue){case jsts.geom.Location.EXTERIOR:return'e';case jsts.geom.Location.BOUNDARY:return'b';case jsts.geom.Location.INTERIOR:return'i';case jsts.geom.Location.NONE:return'-';} +throw new jsts.IllegalArgumentError('Unknown location value: '+ +locationValue);};(function(){var AssertionFailedException=function(message){this.message=message;};AssertionFailedException.prototype=new Error();AssertionFailedException.prototype.name='AssertionFailedException';jsts.util.AssertionFailedException=AssertionFailedException;})();(function(){var AssertionFailedException=jsts.util.AssertionFailedException;jsts.util.Assert=function(){};jsts.util.Assert.isTrue=function(assertion,message){if(!assertion){if(message===null){throw new AssertionFailedException();}else{throw new AssertionFailedException(message);}}};jsts.util.Assert.equals=function(expectedValue,actualValue,message){if(!actualValue.equals(expectedValue)){throw new AssertionFailedException('Expected '+expectedValue+' but encountered '+actualValue+ +(message!=null?': '+message:''));}};jsts.util.Assert.shouldNeverReachHere=function(message){throw new AssertionFailedException('Should never reach here'+ +(message!=null?': '+message:''));};})();(function(){var Location=jsts.geom.Location;var Assert=jsts.util.Assert;var ArrayList=javascript.util.ArrayList;jsts.operation.relate.RelateComputer=function(arg){this.li=new jsts.algorithm.RobustLineIntersector();this.ptLocator=new jsts.algorithm.PointLocator();this.nodes=new jsts.geomgraph.NodeMap(new jsts.operation.relate.RelateNodeFactory());this.isolatedEdges=new ArrayList();this.arg=arg;};jsts.operation.relate.RelateComputer.prototype.li=null;jsts.operation.relate.RelateComputer.prototype.ptLocator=null;jsts.operation.relate.RelateComputer.prototype.arg=null;jsts.operation.relate.RelateComputer.prototype.nodes=null;jsts.operation.relate.RelateComputer.prototype.im=null;jsts.operation.relate.RelateComputer.prototype.isolatedEdges=null;jsts.operation.relate.RelateComputer.prototype.invalidPoint=null;jsts.operation.relate.RelateComputer.prototype.computeIM=function(){var im=new jsts.geom.IntersectionMatrix();im.set(Location.EXTERIOR,Location.EXTERIOR,2);if(!this.arg[0].getGeometry().getEnvelopeInternal().intersects(this.arg[1].getGeometry().getEnvelopeInternal())){this.computeDisjointIM(im);return im;} +this.arg[0].computeSelfNodes(this.li,false);this.arg[1].computeSelfNodes(this.li,false);var intersector=this.arg[0].computeEdgeIntersections(this.arg[1],this.li,false);this.computeIntersectionNodes(0);this.computeIntersectionNodes(1);this.copyNodesAndLabels(0);this.copyNodesAndLabels(1);this.labelIsolatedNodes();this.computeProperIntersectionIM(intersector,im);var eeBuilder=new jsts.operation.relate.EdgeEndBuilder();var ee0=eeBuilder.computeEdgeEnds(this.arg[0].getEdgeIterator());this.insertEdgeEnds(ee0);var ee1=eeBuilder.computeEdgeEnds(this.arg[1].getEdgeIterator());this.insertEdgeEnds(ee1);this.labelNodeEdges();this.labelIsolatedEdges(0,1);this.labelIsolatedEdges(1,0);this.updateIM(im);return im;};jsts.operation.relate.RelateComputer.prototype.insertEdgeEnds=function(ee){for(var i=ee.iterator();i.hasNext();){var e=i.next();this.nodes.add(e);}};jsts.operation.relate.RelateComputer.prototype.computeProperIntersectionIM=function(intersector,im){var dimA=this.arg[0].getGeometry().getDimension();var dimB=this.arg[1].getGeometry().getDimension();var hasProper=intersector.hasProperIntersection();var hasProperInterior=intersector.hasProperInteriorIntersection();if(dimA===2&&dimB===2){if(hasProper) +im.setAtLeast('212101212');} +else if(dimA===2&&dimB===1){if(hasProper) +im.setAtLeast('FFF0FFFF2');if(hasProperInterior) +im.setAtLeast('1FFFFF1FF');}else if(dimA===1&&dimB===2){if(hasProper) +im.setAtLeast('F0FFFFFF2');if(hasProperInterior) +im.setAtLeast('1F1FFFFFF');} +else if(dimA===1&&dimB===1){if(hasProperInterior) +im.setAtLeast('0FFFFFFFF');}};jsts.operation.relate.RelateComputer.prototype.copyNodesAndLabels=function(argIndex){for(var i=this.arg[argIndex].getNodeIterator();i.hasNext();){var graphNode=i.next();var newNode=this.nodes.addNode(graphNode.getCoordinate());newNode.setLabel(argIndex,graphNode.getLabel().getLocation(argIndex));}};jsts.operation.relate.RelateComputer.prototype.computeIntersectionNodes=function(argIndex){for(var i=this.arg[argIndex].getEdgeIterator();i.hasNext();){var e=i.next();var eLoc=e.getLabel().getLocation(argIndex);for(var eiIt=e.getEdgeIntersectionList().iterator();eiIt.hasNext();){var ei=eiIt.next();var n=this.nodes.addNode(ei.coord);if(eLoc===Location.BOUNDARY) +n.setLabelBoundary(argIndex);else{if(n.getLabel().isNull(argIndex)) +n.setLabel(argIndex,Location.INTERIOR);}}}};jsts.operation.relate.RelateComputer.prototype.labelIntersectionNodes=function(argIndex){for(var i=this.arg[argIndex].getEdgeIterator();i.hasNext();){var e=i.next();var eLoc=e.getLabel().getLocation(argIndex);for(var eiIt=e.getEdgeIntersectionList().iterator();eiIt.hasNext();){var ei=eiIt.next();var n=this.nodes.find(ei.coord);if(n.getLabel().isNull(argIndex)){if(eLoc===Location.BOUNDARY) +n.setLabelBoundary(argIndex);else +n.setLabel(argIndex,Location.INTERIOR);}}}};jsts.operation.relate.RelateComputer.prototype.computeDisjointIM=function(im){var ga=this.arg[0].getGeometry();if(!ga.isEmpty()){im.set(Location.INTERIOR,Location.EXTERIOR,ga.getDimension());im.set(Location.BOUNDARY,Location.EXTERIOR,ga.getBoundaryDimension());} +var gb=this.arg[1].getGeometry();if(!gb.isEmpty()){im.set(Location.EXTERIOR,Location.INTERIOR,gb.getDimension());im.set(Location.EXTERIOR,Location.BOUNDARY,gb.getBoundaryDimension());}};jsts.operation.relate.RelateComputer.prototype.labelNodeEdges=function(){for(var ni=this.nodes.iterator();ni.hasNext();){var node=ni.next();node.getEdges().computeLabelling(this.arg);}};jsts.operation.relate.RelateComputer.prototype.updateIM=function(im){for(var ei=this.isolatedEdges.iterator();ei.hasNext();){var e=ei.next();e.updateIM(im);} +for(var ni=this.nodes.iterator();ni.hasNext();){var node=ni.next();node.updateIM(im);node.updateIMFromEdges(im);}};jsts.operation.relate.RelateComputer.prototype.labelIsolatedEdges=function(thisIndex,targetIndex){for(var ei=this.arg[thisIndex].getEdgeIterator();ei.hasNext();){var e=ei.next();if(e.isIsolated()){this.labelIsolatedEdge(e,targetIndex,this.arg[targetIndex].getGeometry());this.isolatedEdges.add(e);}}};jsts.operation.relate.RelateComputer.prototype.labelIsolatedEdge=function(e,targetIndex,target){if(target.getDimension()>0){var loc=this.ptLocator.locate(e.getCoordinate(),target);e.getLabel().setAllLocations(targetIndex,loc);}else{e.getLabel().setAllLocations(targetIndex,Location.EXTERIOR);}};jsts.operation.relate.RelateComputer.prototype.labelIsolatedNodes=function(){for(var ni=this.nodes.iterator();ni.hasNext();){var n=ni.next();var label=n.getLabel();Assert.isTrue(label.getGeometryCount()>0,'node with empty label found');if(n.isIsolated()){if(label.isNull(0)) +this.labelIsolatedNode(n,0);else +this.labelIsolatedNode(n,1);}}};jsts.operation.relate.RelateComputer.prototype.labelIsolatedNode=function(n,targetIndex){var loc=this.ptLocator.locate(n.getCoordinate(),this.arg[targetIndex].getGeometry());n.getLabel().setAllLocations(targetIndex,loc);};})();(function(){var Assert=jsts.util.Assert;jsts.geomgraph.GraphComponent=function(label){this.label=label;};jsts.geomgraph.GraphComponent.prototype.label=null;jsts.geomgraph.GraphComponent.prototype._isInResult=false;jsts.geomgraph.GraphComponent.prototype._isCovered=false;jsts.geomgraph.GraphComponent.prototype._isCoveredSet=false;jsts.geomgraph.GraphComponent.prototype._isVisited=false;jsts.geomgraph.GraphComponent.prototype.getLabel=function(){return this.label;};jsts.geomgraph.GraphComponent.prototype.setLabel=function(label){if(arguments.length===2){this.setLabel2.apply(this,arguments);return;} +this.label=label;};jsts.geomgraph.GraphComponent.prototype.setInResult=function(isInResult){this._isInResult=isInResult;};jsts.geomgraph.GraphComponent.prototype.isInResult=function(){return this._isInResult;};jsts.geomgraph.GraphComponent.prototype.setCovered=function(isCovered){this._isCovered=isCovered;this._isCoveredSet=true;};jsts.geomgraph.GraphComponent.prototype.isCovered=function(){return this._isCovered;};jsts.geomgraph.GraphComponent.prototype.isCoveredSet=function(){return this._isCoveredSet;};jsts.geomgraph.GraphComponent.prototype.isVisited=function(){return this._isVisited;};jsts.geomgraph.GraphComponent.prototype.setVisited=function(isVisited){this._isVisited=isVisited;};jsts.geomgraph.GraphComponent.prototype.getCoordinate=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.geomgraph.GraphComponent.prototype.computeIM=function(im){throw new jsts.error.AbstractMethodInvocationError();};jsts.geomgraph.GraphComponent.prototype.isIsolated=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.geomgraph.GraphComponent.prototype.updateIM=function(im){Assert.isTrue(this.label.getGeometryCount()>=2,'found partial label');this.computeIM(im);};})();jsts.geomgraph.Node=function(coord,edges){this.coord=coord;this.edges=edges;this.label=new jsts.geomgraph.Label(0,jsts.geom.Location.NONE);};jsts.geomgraph.Node.prototype=new jsts.geomgraph.GraphComponent();jsts.geomgraph.Node.prototype.coord=null;jsts.geomgraph.Node.prototype.edges=null;jsts.geomgraph.Node.prototype.isIsolated=function(){return(this.label.getGeometryCount()==1);};jsts.geomgraph.Node.prototype.setLabel2=function(argIndex,onLocation){if(this.label===null){this.label=new jsts.geomgraph.Label(argIndex,onLocation);}else +this.label.setLocation(argIndex,onLocation);};jsts.geomgraph.Node.prototype.setLabelBoundary=function(argIndex){var loc=jsts.geom.Location.NONE;if(this.label!==null) +loc=this.label.getLocation(argIndex);var newLoc;switch(loc){case jsts.geom.Location.BOUNDARY:newLoc=jsts.geom.Location.INTERIOR;break;case jsts.geom.Location.INTERIOR:newLoc=jsts.geom.Location.BOUNDARY;break;default:newLoc=jsts.geom.Location.BOUNDARY;break;} +this.label.setLocation(argIndex,newLoc);};jsts.geomgraph.Node.prototype.add=function(e){this.edges.insert(e);e.setNode(this);};jsts.geomgraph.Node.prototype.getCoordinate=function(){return this.coord;};jsts.geomgraph.Node.prototype.getEdges=function(){return this.edges;};jsts.geomgraph.Node.prototype.isIncidentEdgeInResult=function(){for(var it=this.getEdges().getEdges().iterator();it.hasNext();){var de=it.next();if(de.getEdge().isInResult()) +return true;} +return false;};(function(){var EdgeRing=function(factory){this.deList=new javascript.util.ArrayList();this.factory=factory;};EdgeRing.findEdgeRingContaining=function(testEr,shellList){var testRing=testEr.getRing();var testEnv=testRing.getEnvelopeInternal();var testPt=testRing.getCoordinateN(0);var minShell=null;var minEnv=null;for(var it=shellList.iterator();it.hasNext();){var tryShell=it.next();var tryRing=tryShell.getRing();var tryEnv=tryRing.getEnvelopeInternal();if(minShell!=null) +minEnv=minShell.getRing().getEnvelopeInternal();var isContained=false;if(tryEnv.equals(testEnv)) +continue;testPt=jsts.geom.CoordinateArrays.ptNotInList(testRing.getCoordinates(),tryRing.getCoordinates());if(tryEnv.contains(testEnv)&&jsts.algorithm.CGAlgorithms.isPointInRing(testPt,tryRing.getCoordinates())) +isContained=true;if(isContained){if(minShell==null||minEnv.contains(tryEnv)){minShell=tryShell;}}} +return minShell;};EdgeRing.ptNotInList=function(testPts,pts){for(var i=0;i=0;i--){coordList.add(coords[i],false);}}};jsts.operation.polygonize.EdgeRing=EdgeRing;})();(function(){var GraphComponent=jsts.planargraph.GraphComponent;var Edge=function(de0,de1){if(de0===undefined){return;} +this.setDirectedEdges(de0,de1);};Edge.prototype=new GraphComponent();Edge.prototype.dirEdge=null;Edge.prototype.setDirectedEdges=function(de0,de1){this.dirEdge=[de0,de1];de0.setEdge(this);de1.setEdge(this);de0.setSym(de1);de1.setSym(de0);de0.getFromNode().addOutEdge(de0);de1.getFromNode().addOutEdge(de1);};Edge.prototype.getDirEdge=function(i){if(i instanceof jsts.planargraph.Node){this.getDirEdge2(i);} +return this.dirEdge[i];};Edge.prototype.getDirEdge2=function(fromNode){if(this.dirEdge[0].getFromNode()==fromNode) +return this.dirEdge[0];if(this.dirEdge[1].getFromNode()==fromNode) +return this.dirEdge[1];return null;};Edge.prototype.getOppositeNode=function(node){if(this.dirEdge[0].getFromNode()==node) +return this.dirEdge[0].getToNode();if(this.dirEdge[1].getFromNode()==node) +return this.dirEdge[1].getToNode();return null;};Edge.prototype.remove=function(){this.dirEdge=null;};Edge.prototype.isRemoved=function(){return dirEdge==null;};jsts.planargraph.Edge=Edge;})();jsts.operation.polygonize.PolygonizeEdge=function(line){this.line=line;};jsts.operation.polygonize.PolygonizeEdge.prototype=new jsts.planargraph.Edge();jsts.operation.polygonize.PolygonizeEdge.prototype.line=null;jsts.operation.polygonize.PolygonizeEdge.prototype.getLine=function(){return this.line;};(function(){var ArrayList=javascript.util.ArrayList;var GraphComponent=jsts.planargraph.GraphComponent;var DirectedEdge=function(from,to,directionPt,edgeDirection){if(from===undefined){return;} +this.from=from;this.to=to;this.edgeDirection=edgeDirection;this.p0=from.getCoordinate();this.p1=directionPt;var dx=this.p1.x-this.p0.x;var dy=this.p1.y-this.p0.y;this.quadrant=jsts.geomgraph.Quadrant.quadrant(dx,dy);this.angle=Math.atan2(dy,dx);};DirectedEdge.prototype=new GraphComponent();DirectedEdge.toEdges=function(dirEdges){var edges=new ArrayList();for(var i=dirEdges.iterator();i.hasNext();){edges.add((i.next()).parentEdge);} +return edges;};DirectedEdge.prototype.parentEdge=null;DirectedEdge.prototype.from=null;DirectedEdge.prototype.to=null;DirectedEdge.prototype.p0=null;DirectedEdge.prototype.p1=null;DirectedEdge.prototype.sym=null;DirectedEdge.prototype.edgeDirection=null;DirectedEdge.prototype.quadrant=null;DirectedEdge.prototype.angle=null;DirectedEdge.prototype.getEdge=function(){return this.parentEdge;};DirectedEdge.prototype.setEdge=function(parentEdge){this.parentEdge=parentEdge;};DirectedEdge.prototype.getQuadrant=function(){return this.quadrant;};DirectedEdge.prototype.getDirectionPt=function(){return this.p1;};DirectedEdge.prototype.getEdgeDirection=function(){return this.edgeDirection;};DirectedEdge.prototype.getFromNode=function(){return this.from;};DirectedEdge.prototype.getToNode=function(){return this.to;};DirectedEdge.prototype.getCoordinate=function(){return this.from.getCoordinate();};DirectedEdge.prototype.getAngle=function(){return this.angle;};DirectedEdge.prototype.getSym=function(){return this.sym;};DirectedEdge.prototype.setSym=function(sym){this.sym=sym;};DirectedEdge.prototype.remove=function(){this.sym=null;this.parentEdge=null;};DirectedEdge.prototype.isRemoved=function(){return this.parentEdge==null;};DirectedEdge.prototype.compareTo=function(obj){var de=obj;return this.compareDirection(de);};DirectedEdge.prototype.compareDirection=function(e){if(this.quadrant>e.quadrant) +return 1;if(this.quadrant1){if(intNodes==null) +intNodes=new ArrayList();intNodes.add(node);} +de=de.getNext();Assert.isTrue(de!=null,'found null DE in ring');Assert.isTrue(de==startDE||!de.isInRing(),'found DE already in ring');}while(de!=startDE);return intNodes;};PolygonizeGraph.prototype.getEdgeRings=function(){this.computeNextCWEdges();PolygonizeGraph.label(this.dirEdges,-1);var maximalRings=PolygonizeGraph.findLabeledEdgeRings(this.dirEdges);this.convertMaximalToMinimalEdgeRings(maximalRings);var edgeRingList=new ArrayList();for(var i=this.dirEdges.iterator();i.hasNext();){var de=i.next();if(de.isMarked()) +continue;if(de.isInRing()) +continue;var er=this.findEdgeRing(de);edgeRingList.add(er);} +return edgeRingList;};PolygonizeGraph.findLabeledEdgeRings=function(dirEdges){var edgeRingStarts=new ArrayList();var currLabel=1;for(var i=dirEdges.iterator();i.hasNext();){var de=i.next();if(de.isMarked()) +continue;if(de.getLabel()>=0) +continue;edgeRingStarts.add(de);var edges=PolygonizeGraph.findDirEdgesInRing(de);PolygonizeGraph.label(edges,currLabel);currLabel++;} +return edgeRingStarts;};PolygonizeGraph.prototype.deleteCutEdges=function(){this.computeNextCWEdges();PolygonizeGraph.findLabeledEdgeRings(this.dirEdges);var cutLines=new ArrayList();for(var i=this.dirEdges.iterator();i.hasNext();){var de=i.next();if(de.isMarked()) +continue;var sym=de.getSym();if(de.getLabel()==sym.getLabel()){de.setMarked(true);sym.setMarked(true);var e=de.getEdge();cutLines.add(e.getLine());}} +return cutLines;};PolygonizeGraph.label=function(dirEdges,label){for(var i=dirEdges.iterator();i.hasNext();){var de=i.next();de.setLabel(label);}};PolygonizeGraph.computeNextCWEdges=function(node){var deStar=node.getOutEdges();var startDE=null;var prevDE=null;for(var i=deStar.getEdges().iterator();i.hasNext();){var outDE=i.next();if(outDE.isMarked()) +continue;if(startDE==null) +startDE=outDE;if(prevDE!=null){var sym=prevDE.getSym();sym.setNext(outDE);} +prevDE=outDE;} +if(prevDE!=null){var sym=prevDE.getSym();sym.setNext(startDE);}};PolygonizeGraph.computeNextCCWEdges=function(node,label){var deStar=node.getOutEdges();var firstOutDE=null;var prevInDE=null;var edges=deStar.getEdges();for(var i=edges.size()-1;i>=0;i--){var de=edges.get(i);var sym=de.getSym();var outDE=null;if(de.getLabel()==label) +outDE=de;var inDE=null;if(sym.getLabel()==label) +inDE=sym;if(outDE==null&&inDE==null) +continue;if(inDE!=null){prevInDE=inDE;} +if(outDE!=null){if(prevInDE!=null){prevInDE.setNext(outDE);prevInDE=null;} +if(firstOutDE==null) +firstOutDE=outDE;}} +if(prevInDE!=null){Assert.isTrue(firstOutDE!=null);prevInDE.setNext(firstOutDE);}};PolygonizeGraph.findDirEdgesInRing=function(startDE){var de=startDE;var edges=new ArrayList();do{edges.add(de);de=de.getNext();Assert.isTrue(de!=null,'found null DE in ring');Assert.isTrue(de==startDE||!de.isInRing(),'found DE already in ring');}while(de!=startDE);return edges;};PolygonizeGraph.prototype.findEdgeRing=function(startDE){var de=startDE;var er=new EdgeRing(this.factory);do{er.add(de);de.setRing(er);de=de.getNext();Assert.isTrue(de!=null,'found null DE in ring');Assert.isTrue(de==startDE||!de.isInRing(),'found DE already in ring');}while(de!=startDE);return er;};PolygonizeGraph.prototype.deleteDangles=function(){var nodesToRemove=this.findNodesOfDegree(1);var dangleLines=new HashSet();var nodeStack=new Stack();for(var i=nodesToRemove.iterator();i.hasNext();){nodeStack.push(i.next());} +while(!nodeStack.isEmpty()){var node=nodeStack.pop();PolygonizeGraph.deleteAllEdges(node);var nodeOutEdges=node.getOutEdges().getEdges();for(var i=nodeOutEdges.iterator();i.hasNext();){var de=i.next();de.setMarked(true);var sym=de.getSym();if(sym!=null) +sym.setMarked(true);var e=de.getEdge();dangleLines.add(e.getLine());var toNode=de.getToNode();if(PolygonizeGraph.getDegreeNonDeleted(toNode)==1) +nodeStack.push(toNode);}} +return dangleLines;};PolygonizeGraph.prototype.computeDepthParity=function(){while(true){var de=null;if(de==null) +return;this.computeDepthParity(de);}};PolygonizeGraph.prototype.computeDepthParity=function(de){};jsts.operation.polygonize.PolygonizeGraph=PolygonizeGraph;})();jsts.index.strtree.Interval=function(){var other;if(arguments.length===1){other=arguments[0];return jsts.index.strtree.Interval(other.min,other.max);}else if(arguments.length===2){jsts.util.Assert.isTrue(this.min<=this.max);this.min=arguments[0];this.max=arguments[1];}};jsts.index.strtree.Interval.prototype.min=null;jsts.index.strtree.Interval.prototype.max=null;jsts.index.strtree.Interval.prototype.getCentre=function(){return(this.min+this.max)/2;};jsts.index.strtree.Interval.prototype.expandToInclude=function(other){this.max=Math.max(this.max,other.max);this.min=Math.min(this.min,other.min);return this;};jsts.index.strtree.Interval.prototype.intersects=function(other){return!(other.min>this.max||other.max1;if(isCollection){if(geom0 instanceof jsts.geom.Polygon){return this.createMultiPolygon(geomList.toArray());}else if(geom0 instanceof jsts.geom.LineString){return this.createMultiLineString(geomList.toArray());}else if(geom0 instanceof jsts.geom.Point){return this.createMultiPoint(geomList.toArray());} +jsts.util.Assert.shouldNeverReachHere('Unhandled class: '+geom0);} +return geom0;};jsts.geom.GeometryFactory.prototype.createGeometryCollection=function(geometries){return new jsts.geom.GeometryCollection(geometries,this);};jsts.geom.GeometryFactory.prototype.toGeometry=function(envelope){if(envelope.isNull()){return this.createPoint(null);} +if(envelope.getMinX()===envelope.getMaxX()&&envelope.getMinY()===envelope.getMaxY()){return this.createPoint(new jsts.geom.Coordinate(envelope.getMinX(),envelope.getMinY()));} +if(envelope.getMinX()===envelope.getMaxX()||envelope.getMinY()===envelope.getMaxY()){return this.createLineString([new jsts.geom.Coordinate(envelope.getMinX(),envelope.getMinY()),new jsts.geom.Coordinate(envelope.getMaxX(),envelope.getMaxY())]);} +return this.createPolygon(this.createLinearRing([new jsts.geom.Coordinate(envelope.getMinX(),envelope.getMinY()),new jsts.geom.Coordinate(envelope.getMinX(),envelope.getMaxY()),new jsts.geom.Coordinate(envelope.getMaxX(),envelope.getMaxY()),new jsts.geom.Coordinate(envelope.getMaxX(),envelope.getMinY()),new jsts.geom.Coordinate(envelope.getMinX(),envelope.getMinY())]),null);};jsts.geomgraph.NodeFactory=function(){};jsts.geomgraph.NodeFactory.prototype.createNode=function(coord){return new jsts.geomgraph.Node(coord,null);};(function(){jsts.geomgraph.Position=function(){};jsts.geomgraph.Position.ON=0;jsts.geomgraph.Position.LEFT=1;jsts.geomgraph.Position.RIGHT=2;jsts.geomgraph.Position.opposite=function(position){if(position===jsts.geomgraph.Position.LEFT){return jsts.geomgraph.Position.RIGHT;} +if(position===jsts.geomgraph.Position.RIGHT){return jsts.geomgraph.Position.LEFT;} +return position;};})();jsts.geomgraph.TopologyLocation=function(){this.location=[];if(arguments.length===3){var on=arguments[0];var left=arguments[1];var right=arguments[2];this.init(3);this.location[jsts.geomgraph.Position.ON]=on;this.location[jsts.geomgraph.Position.LEFT]=left;this.location[jsts.geomgraph.Position.RIGHT]=right;}else if(arguments[0]instanceof jsts.geomgraph.TopologyLocation){var gl=arguments[0];this.init(gl.location.length);if(gl!=null){for(var i=0;i1;};jsts.geomgraph.TopologyLocation.prototype.isLine=function(){return this.location.length===1;};jsts.geomgraph.TopologyLocation.prototype.flip=function(){if(this.location.length<=1) +return;var temp=this.location[jsts.geomgraph.Position.LEFT];this.location[jsts.geomgraph.Position.LEFT]=this.location[jsts.geomgraph.Position.RIGHT];this.location[jsts.geomgraph.Position.RIGHT]=temp;};jsts.geomgraph.TopologyLocation.prototype.setAllLocations=function(locValue){for(var i=0;ithis.location.length){var newLoc=[];newLoc[jsts.geomgraph.Position.ON]=this.location[jsts.geomgraph.Position.ON];newLoc[jsts.geomgraph.Position.LEFT]=jsts.geom.Location.NONE;newLoc[jsts.geomgraph.Position.RIGHT]=jsts.geom.Location.NONE;this.location=newLoc;} +for(var i=0;ithis.maxNodeDegree) +this.maxNodeDegree=degree;de=this.getNext(de);}while(de!==this.startDe);this.maxNodeDegree*=2;};jsts.geomgraph.EdgeRing.prototype.setInResult=function(){var de=this.startDe;do{de.getEdge().setInResult(true);de=de.getNext();}while(de!=this.startDe);};jsts.geomgraph.EdgeRing.prototype.mergeLabel=function(deLabel){this.mergeLabel2(deLabel,0);this.mergeLabel2(deLabel,1);};jsts.geomgraph.EdgeRing.prototype.mergeLabel2=function(deLabel,geomIndex){var loc=deLabel.getLocation(geomIndex,jsts.geomgraph.Position.RIGHT);if(loc==jsts.geom.Location.NONE) +return;if(this.label.getLocation(geomIndex)===jsts.geom.Location.NONE){this.label.setLocation(geomIndex,loc);return;}};jsts.geomgraph.EdgeRing.prototype.addPoints=function(edge,isForward,isFirstEdge){var edgePts=edge.getCoordinates();if(isForward){var startIndex=1;if(isFirstEdge) +startIndex=0;for(var i=startIndex;i=0;i--){this.pts.push(edgePts[i]);}}};jsts.geomgraph.EdgeRing.prototype.containsPoint=function(p){var shell=this.getLinearRing();var env=shell.getEnvelopeInternal();if(!env.contains(p)) +return false;if(!jsts.algorithm.CGAlgorithms.isPointInRing(p,shell.getCoordinates())) +return false;for(var i=0;i0){this.points.reverse();} +return;}}};jsts.geom.LineString.prototype.CLASS_NAME='jsts.geom.LineString';})();(function(){jsts.geom.LinearRing=function(points,factory){jsts.geom.LineString.apply(this,arguments);};jsts.geom.LinearRing.prototype=new jsts.geom.LineString();jsts.geom.LinearRing.constructor=jsts.geom.LinearRing;jsts.geom.LinearRing.prototype.getBoundaryDimension=function(){return jsts.geom.Dimension.FALSE;};jsts.geom.LinearRing.prototype.isSimple=function(){return true;};jsts.geom.LinearRing.prototype.getGeometryType=function(){return'LinearRing';};jsts.geom.LinearRing.MINIMUM_VALID_SIZE=4;jsts.geom.LinearRing.prototype.CLASS_NAME='jsts.geom.LinearRing';})();jsts.operation.overlay.OverlayNodeFactory=function(){};jsts.operation.overlay.OverlayNodeFactory.prototype=new jsts.geomgraph.NodeFactory();jsts.operation.overlay.OverlayNodeFactory.constructor=jsts.operation.overlay.OverlayNodeFactory;jsts.operation.overlay.OverlayNodeFactory.prototype.createNode=function(coord){return new jsts.geomgraph.Node(coord,new jsts.geomgraph.DirectedEdgeStar());};jsts.operation.buffer.SubgraphDepthLocater=function(subgraphs){this.subgraphs=[];this.seg=new jsts.geom.LineSegment();this.subgraphs=subgraphs;};jsts.operation.buffer.SubgraphDepthLocater.prototype.subgraphs=null;jsts.operation.buffer.SubgraphDepthLocater.prototype.seg=null;jsts.operation.buffer.SubgraphDepthLocater.prototype.getDepth=function(p){var stabbedSegments=this.findStabbedSegments(p);if(stabbedSegments.length===0) +return 0;stabbedSegments.sort();var ds=stabbedSegments[0];return ds.leftDepth;};jsts.operation.buffer.SubgraphDepthLocater.prototype.findStabbedSegments=function(stabbingRayLeftPt){if(arguments.length===3){this.findStabbedSegments2.apply(this,arguments);return;} +var stabbedSegments=[];for(var i=0;ienv.getMaxY()) +continue;this.findStabbedSegments2(stabbingRayLeftPt,bsg.getDirectedEdges(),stabbedSegments);} +return stabbedSegments;};jsts.operation.buffer.SubgraphDepthLocater.prototype.findStabbedSegments2=function(stabbingRayLeftPt,dirEdges,stabbedSegments){if(arguments[1]instanceof jsts.geomgraph.DirectedEdge){this.findStabbedSegments3(stabbingRayLeftPt,dirEdges,stabbedSegments);return;} +for(var i=dirEdges.iterator();i.hasNext();){var de=i.next();if(!de.isForward()) +continue;this.findStabbedSegments3(stabbingRayLeftPt,de,stabbedSegments);}};jsts.operation.buffer.SubgraphDepthLocater.prototype.findStabbedSegments3=function(stabbingRayLeftPt,dirEdge,stabbedSegments){var pts=dirEdge.getEdge().getCoordinates();for(var i=0;ithis.seg.p1.y) +this.seg.reverse();var maxx=Math.max(this.seg.p0.x,this.seg.p1.x);if(maxxthis.seg.p1.y) +continue;if(jsts.algorithm.CGAlgorithms.computeOrientation(this.seg.p0,this.seg.p1,stabbingRayLeftPt)===jsts.algorithm.CGAlgorithms.RIGHT) +continue;var depth=dirEdge.getDepth(jsts.geomgraph.Position.LEFT);if(!this.seg.p0.equals(pts[i])) +depth=dirEdge.getDepth(jsts.geomgraph.Position.RIGHT);var ds=new jsts.operation.buffer.SubgraphDepthLocater.DepthSegment(this.seg,depth);stabbedSegments.push(ds);}};jsts.operation.buffer.SubgraphDepthLocater.DepthSegment=function(seg,depth){this.upwardSeg=new jsts.geom.LineSegment(seg);this.leftDepth=depth;};jsts.operation.buffer.SubgraphDepthLocater.DepthSegment.prototype.upwardSeg=null;jsts.operation.buffer.SubgraphDepthLocater.DepthSegment.prototype.leftDepth=null;jsts.operation.buffer.SubgraphDepthLocater.DepthSegment.prototype.compareTo=function(obj){var other=obj;var orientIndex=this.upwardSeg.orientationIndex(other.upwardSeg);if(orientIndex===0) +orientIndex=-1*other.upwardSeg.orientationIndex(upwardSeg);if(orientIndex!==0) +return orientIndex;return this.compareX(this.upwardSeg,other.upwardSeg);};jsts.operation.buffer.SubgraphDepthLocater.DepthSegment.prototype.compareX=function(seg0,seg1){var compare0=seg0.p0.compareTo(seg1.p0);if(compare0!==0) +return compare0;return seg0.p1.compareTo(seg1.p1);};jsts.index.ItemVisitor=function(){};jsts.index.ItemVisitor.prototype.visitItem=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.simplify.LineSegmentIndex=function(){this.index=new jsts.index.quadtree.Quadtree();};jsts.simplify.LineSegmentIndex.prototype.index=null;jsts.simplify.LineSegmentIndex.prototype.add=function(line){if(line instanceof jsts.geom.LineSegment){this.add2(line);return;} +var segs=line.getSegments();for(var i=0;ix1) +return 1;return 0;};jsts.noding.SegmentPointComparator.compareValue=function(compareSign0,compareSign1){if(compareSign0<0) +return-1;if(compareSign0>0) +return 1;if(compareSign1<0) +return-1;if(compareSign1>0) +return 1;return 0;};jsts.operation.IsSimpleOp=function(geom){this.geom=geom;};jsts.operation.IsSimpleOp.prototype.geom=null;jsts.operation.IsSimpleOp.prototype.isClosedEndpointsInInterior=true;jsts.operation.IsSimpleOp.prototype.nonSimpleLocation=null;jsts.operation.IsSimpleOp.prototype.IsSimpleOp=function(geom){this.geom=geom;};jsts.operation.IsSimpleOp.prototype.isSimple=function(){this.nonSimpleLocation=null;if(this.geom instanceof jsts.geom.LineString){return this.isSimpleLinearGeometry(this.geom);} +if(this.geom instanceof jsts.geom.MultiLineString){return this.isSimpleLinearGeometry(this.geom);} +if(this.geom instanceof jsts.geom.MultiPoint){return this.isSimpleMultiPoint(this.geom);} +return true;};jsts.operation.IsSimpleOp.prototype.isSimpleMultiPoint=function(mp){if(mp.isEmpty()) +return true;var points=[];for(var i=0;i1&&snapPts[0].equals2D(snapPts[snapPts.length-1])){distinctPtCount=snapPts.length-1;} +i=0;for(i;i=0){srcCoords.add(index+1,new jsts.geom.Coordinate(snapPt),false);}}};LineStringSnapper.prototype.findSegmentIndexToSnap=function(snapPt,srcCoords){var minDist=Number.MAX_VALUE,snapIndex=-1,i=0,dist;for(i;i0.0?distance:0.0;var bufEnvSize=envSize+2*expandByDistance;var bufEnvLog10=(Math.log(bufEnvSize)/Math.log(10)+1.0);var minUnitLog10=bufEnvLog10-maxPrecisionDigits;var scaleFactor=Math.pow(10.0,-minUnitLog10);return scaleFactor;};jsts.operation.buffer.BufferOp.bufferOp=function(g,distance){if(arguments.length>2){return jsts.operation.buffer.BufferOp.bufferOp2.apply(this,arguments);} +var gBuf=new jsts.operation.buffer.BufferOp(g);var geomBuf=gBuf.getResultGeometry(distance);return geomBuf;};jsts.operation.buffer.BufferOp.bufferOp2=function(g,distance,params){if(arguments.length>3){return jsts.operation.buffer.BufferOp.bufferOp3.apply(this,arguments);} +var bufOp=new jsts.operation.buffer.BufferOp(g,params);var geomBuf=bufOp.getResultGeometry(distance);return geomBuf;};jsts.operation.buffer.BufferOp.bufferOp3=function(g,distance,quadrantSegments){if(arguments.length>4){return jsts.operation.buffer.BufferOp.bufferOp4.apply(this,arguments);} +var bufOp=new jsts.operation.buffer.BufferOp(g);bufOp.setQuadrantSegments(quadrantSegments);var geomBuf=bufOp.getResultGeometry(distance);return geomBuf;};jsts.operation.buffer.BufferOp.bufferOp4=function(g,distance,quadrantSegments,endCapStyle){var bufOp=new jsts.operation.buffer.BufferOp(g);bufOp.setQuadrantSegments(quadrantSegments);bufOp.setEndCapStyle(endCapStyle);var geomBuf=bufOp.getResultGeometry(distance);return geomBuf;};jsts.operation.buffer.BufferOp.prototype.argGeom=null;jsts.operation.buffer.BufferOp.prototype.distance=null;jsts.operation.buffer.BufferOp.prototype.bufParams=null;jsts.operation.buffer.BufferOp.prototype.resultGeometry=null;jsts.operation.buffer.BufferOp.prototype.setEndCapStyle=function(endCapStyle){this.bufParams.setEndCapStyle(endCapStyle);};jsts.operation.buffer.BufferOp.prototype.setQuadrantSegments=function(quadrantSegments){this.bufParams.setQuadrantSegments(quadrantSegments);};jsts.operation.buffer.BufferOp.prototype.getResultGeometry=function(dist){this.distance=dist;this.computeGeometry();return this.resultGeometry;};jsts.operation.buffer.BufferOp.prototype.computeGeometry=function(){this.bufferOriginalPrecision();if(this.resultGeometry!==null){return;} +var argPM=this.argGeom.getPrecisionModel();if(argPM.getType()===jsts.geom.PrecisionModel.FIXED){this.bufferFixedPrecision(argPM);}else{this.bufferReducedPrecision();}};jsts.operation.buffer.BufferOp.prototype.bufferReducedPrecision=function(){var precDigits;var saveException=null;for(precDigits=jsts.operation.buffer.BufferOp.MAX_PRECISION_DIGITS;precDigits>=0;precDigits--){try{this.bufferReducedPrecision2(precDigits);}catch(ex){saveException=ex;} +if(this.resultGeometry!==null){return;}} +throw saveException;};jsts.operation.buffer.BufferOp.prototype.bufferOriginalPrecision=function(){try{var bufBuilder=new jsts.operation.buffer.BufferBuilder(this.bufParams);this.resultGeometry=bufBuilder.buffer(this.argGeom,this.distance);}catch(e){}};jsts.operation.buffer.BufferOp.prototype.bufferReducedPrecision2=function(precisionDigits){var sizeBasedScaleFactor=jsts.operation.buffer.BufferOp.precisionScaleFactor(this.argGeom,this.distance,precisionDigits);var fixedPM=new jsts.geom.PrecisionModel(sizeBasedScaleFactor);this.bufferFixedPrecision(fixedPM);};jsts.operation.buffer.BufferOp.prototype.bufferFixedPrecision=function(fixedPM){var noder=new jsts.noding.ScaledNoder(new jsts.noding.snapround.MCIndexSnapRounder(new jsts.geom.PrecisionModel(1.0)),fixedPM.getScale());var bufBuilder=new jsts.operation.buffer.BufferBuilder(this.bufParams);bufBuilder.setWorkingPrecisionModel(fixedPM);bufBuilder.setNoder(noder);this.resultGeometry=bufBuilder.buffer(this.argGeom,this.distance);};jsts.geomgraph.index.EdgeSetIntersector=function(){};jsts.geomgraph.index.EdgeSetIntersector.prototype.computeIntersections=function(edges,si,testAllSegments){throw new jsts.error.AbstractMethodInvocationError();};jsts.geomgraph.index.EdgeSetIntersector.prototype.computeIntersections2=function(edges0,edges1,si){throw new jsts.error.AbstractMethodInvocationError();};jsts.geomgraph.index.SimpleMCSweepLineIntersector=function(){throw new jsts.error.NotImplementedError();};jsts.geomgraph.index.SimpleMCSweepLineIntersector.prototype=new jsts.geomgraph.index.EdgeSetIntersector();jsts.algorithm.locate.SimplePointInAreaLocator=function(geom){this.geom=geom;};jsts.algorithm.locate.SimplePointInAreaLocator.locate=function(p,geom){if(geom.isEmpty()) +return jsts.geom.Location.EXTERIOR;if(jsts.algorithm.locate.SimplePointInAreaLocator.containsPoint(p,geom)) +return jsts.geom.Location.INTERIOR;return jsts.geom.Location.EXTERIOR;};jsts.algorithm.locate.SimplePointInAreaLocator.containsPoint=function(p,geom){if(geom instanceof jsts.geom.Polygon){return jsts.algorithm.locate.SimplePointInAreaLocator.containsPointInPolygon(p,geom);}else if(geom instanceof jsts.geom.GeometryCollection||geom instanceof jsts.geom.MultiPoint||geom instanceof jsts.geom.MultiLineString||geom instanceof jsts.geom.MultiPolygon){for(var i=0;i=0;i--){var nextOut=this.resultAreaEdgeList.get(i);var nextIn=nextOut.getSym();if(firstOut===null&&nextOut.getEdgeRing()===er) +firstOut=nextOut;switch(state){case this.SCANNING_FOR_INCOMING:if(nextIn.getEdgeRing()!=er) +continue;incoming=nextIn;state=this.LINKING_TO_OUTGOING;break;case this.LINKING_TO_OUTGOING:if(nextOut.getEdgeRing()!==er) +continue;incoming.setNextMin(nextOut);state=this.SCANNING_FOR_INCOMING;break;}} +if(state===this.LINKING_TO_OUTGOING){Assert.isTrue(firstOut!==null,'found null for first outgoing dirEdge');Assert.isTrue(firstOut.getEdgeRing()===er,'unable to link last incoming dirEdge');incoming.setNextMin(firstOut);}};jsts.geomgraph.DirectedEdgeStar.prototype.linkAllDirectedEdges=function(){this.getEdges();var prevOut=null;var firstIn=null;for(var i=this.edgeList.size()-1;i>=0;i--){var nextOut=this.edgeList.get(i);var nextIn=nextOut.getSym();if(firstIn===null) +firstIn=nextIn;if(prevOut!==null) +nextIn.setNext(prevOut);prevOut=nextOut;} +firstIn.setNext(prevOut);};jsts.geomgraph.DirectedEdgeStar.prototype.findCoveredLineEdges=function(){var startLoc=Location.NONE;for(var it=this.iterator();it.hasNext();){var nextOut=it.next();var nextIn=nextOut.getSym();if(!nextOut.isLineEdge()){if(nextOut.isInResult()){startLoc=Location.INTERIOR;break;} +if(nextIn.isInResult()){startLoc=Location.EXTERIOR;break;}}} +if(startLoc===Location.NONE) +return;var currLoc=startLoc;for(var it=this.iterator();it.hasNext();){var nextOut=it.next();var nextIn=nextOut.getSym();if(nextOut.isLineEdge()){nextOut.getEdge().setCovered(currLoc===Location.INTERIOR);}else{if(nextOut.isInResult()) +currLoc=Location.EXTERIOR;if(nextIn.isInResult()) +currLoc=Location.INTERIOR;}}};jsts.geomgraph.DirectedEdgeStar.prototype.computeDepths=function(de){if(arguments.length===2){this.computeDepths2.apply(this,arguments);return;} +var edgeIndex=this.findIndex(de);var label=de.getLabel();var startDepth=de.getDepth(Position.LEFT);var targetLastDepth=de.getDepth(Position.RIGHT);var nextDepth=this.computeDepths2(edgeIndex+1,this.edgeList.size(),startDepth);var lastDepth=this.computeDepths2(0,edgeIndex,nextDepth);if(lastDepth!=targetLastDepth) +throw new jsts.error.TopologyError('depth mismatch at '+ +de.getCoordinate());};jsts.geomgraph.DirectedEdgeStar.prototype.computeDepths2=function(startIndex,endIndex,startDepth){var currDepth=startDepth;for(var i=startIndex;i0||this.isIn) +return jsts.geom.Location.INTERIOR;return jsts.geom.Location.EXTERIOR;};jsts.algorithm.PointLocator.prototype.computeLocation=function(p,geom){if(geom instanceof jsts.geom.Point||geom instanceof jsts.geom.LineString||geom instanceof jsts.geom.Polygon){this.updateLocationInfo(this.locate(p,geom));}else if(geom instanceof jsts.geom.MultiLineString){var ml=geom;for(var i=0;idy){dist=dx;}else{dist=dy;}}else{var pdx=Math.abs(p.x-p0.x);var pdy=Math.abs(p.y-p0.y);if(dx>dy){dist=pdx;}else{dist=pdy;} +if(dist===0.0&&!p.equals(p0)){dist=Math.max(pdx,pdy);}} +if(dist===0.0&&!p.equals(p0)){throw new jsts.error.IllegalArgumentError('Bad distance calculation');} +return dist;};jsts.algorithm.LineIntersector.nonRobustComputeEdgeDistance=function(p,p1,p2){var dx=p.x-p1.x;var dy=p.y-p1.y;var dist=Math.sqrt(dx*dx+dy*dy);if(!(dist===0.0&&!p.equals(p1))){throw new jsts.error.IllegalArgumentError('Invalid distance calculation');} +return dist;};jsts.algorithm.LineIntersector.prototype.result=null;jsts.algorithm.LineIntersector.prototype.inputLines=null;jsts.algorithm.LineIntersector.prototype.intPt=null;jsts.algorithm.LineIntersector.prototype.intLineIndex=null;jsts.algorithm.LineIntersector.prototype._isProper=null;jsts.algorithm.LineIntersector.prototype.pa=null;jsts.algorithm.LineIntersector.prototype.pb=null;jsts.algorithm.LineIntersector.prototype.precisionModel=null;jsts.algorithm.LineIntersector.prototype.computeIntersection=function(p,p1,p2){throw new jsts.error.AbstractMethodInvocationError();};jsts.algorithm.LineIntersector.prototype.isCollinear=function(){return this.result===jsts.algorithm.LineIntersector.COLLINEAR_INTERSECTION;};jsts.algorithm.LineIntersector.prototype.computeIntersection=function(p1,p2,p3,p4){this.inputLines[0][0]=p1;this.inputLines[0][1]=p2;this.inputLines[1][0]=p3;this.inputLines[1][1]=p4;this.result=this.computeIntersect(p1,p2,p3,p4);};jsts.algorithm.LineIntersector.prototype.computeIntersect=function(p1,p2,q1,q2){throw new jsts.error.AbstractMethodInvocationError();};jsts.algorithm.LineIntersector.prototype.isEndPoint=function(){return this.hasIntersection()&&!this._isProper;};jsts.algorithm.LineIntersector.prototype.hasIntersection=function(){return this.result!==jsts.algorithm.LineIntersector.NO_INTERSECTION;};jsts.algorithm.LineIntersector.prototype.getIntersectionNum=function(){return this.result;};jsts.algorithm.LineIntersector.prototype.getIntersection=function(intIndex){return this.intPt[intIndex];};jsts.algorithm.LineIntersector.prototype.computeIntLineIndex=function(){if(this.intLineIndex===null){this.intLineIndex=[[],[]];this.computeIntLineIndex(0);this.computeIntLineIndex(1);}};jsts.algorithm.LineIntersector.prototype.isIntersection=function(pt){var i;for(i=0;idist1){this.intLineIndex[segmentIndex][0]=0;this.intLineIndex[segmentIndex][1]=1;}else{this.intLineIndex[segmentIndex][0]=1;this.intLineIndex[segmentIndex][1]=0;}};jsts.algorithm.LineIntersector.prototype.getEdgeDistance=function(segmentIndex,intIndex){var dist=jsts.algorithm.LineIntersector.computeEdgeDistance(this.intPt[intIndex],this.inputLines[segmentIndex][0],this.inputLines[segmentIndex][1]);return dist;};jsts.algorithm.RobustLineIntersector=function(){jsts.algorithm.RobustLineIntersector.prototype.constructor.call(this);};jsts.algorithm.RobustLineIntersector.prototype=new jsts.algorithm.LineIntersector();jsts.algorithm.RobustLineIntersector.prototype.computeIntersection=function(p,p1,p2){if(arguments.length===4){jsts.algorithm.LineIntersector.prototype.computeIntersection.apply(this,arguments);return;} +this._isProper=false;if(jsts.geom.Envelope.intersects(p1,p2,p)){if((jsts.algorithm.CGAlgorithms.orientationIndex(p1,p2,p)===0)&&(jsts.algorithm.CGAlgorithms.orientationIndex(p2,p1,p)===0)){this._isProper=true;if(p.equals(p1)||p.equals(p2)){this._isProper=false;} +this.result=jsts.algorithm.LineIntersector.POINT_INTERSECTION;return;}} +this.result=jsts.algorithm.LineIntersector.NO_INTERSECTION;};jsts.algorithm.RobustLineIntersector.prototype.computeIntersect=function(p1,p2,q1,q2){this._isProper=false;if(!jsts.geom.Envelope.intersects(p1,p2,q1,q2)){return jsts.algorithm.LineIntersector.NO_INTERSECTION;} +var Pq1=jsts.algorithm.CGAlgorithms.orientationIndex(p1,p2,q1);var Pq2=jsts.algorithm.CGAlgorithms.orientationIndex(p1,p2,q2);if((Pq1>0&&Pq2>0)||(Pq1<0&&Pq2<0)){return jsts.algorithm.LineIntersector.NO_INTERSECTION;} +var Qp1=jsts.algorithm.CGAlgorithms.orientationIndex(q1,q2,p1);var Qp2=jsts.algorithm.CGAlgorithms.orientationIndex(q1,q2,p2);if((Qp1>0&&Qp2>0)||(Qp1<0&&Qp2<0)){return jsts.algorithm.LineIntersector.NO_INTERSECTION;} +var collinear=Pq1===0&&Pq2===0&&Qp1===0&&Qp2===0;if(collinear){return this.computeCollinearIntersection(p1,p2,q1,q2);} +if(Pq1===0||Pq2===0||Qp1===0||Qp2===0){this._isProper=false;if(p1.equals2D(q1)||p1.equals2D(q2)){this.intPt[0]=p1;}else if(p2.equals2D(q1)||p2.equals2D(q2)){this.intPt[0]=p2;} +else if(Pq1===0){this.intPt[0]=new jsts.geom.Coordinate(q1);}else if(Pq2===0){this.intPt[0]=new jsts.geom.Coordinate(q2);}else if(Qp1===0){this.intPt[0]=new jsts.geom.Coordinate(p1);}else if(Qp2===0){this.intPt[0]=new jsts.geom.Coordinate(p2);}}else{this._isProper=true;this.intPt[0]=this.intersection(p1,p2,q1,q2);} +return jsts.algorithm.LineIntersector.POINT_INTERSECTION;};jsts.algorithm.RobustLineIntersector.prototype.computeCollinearIntersection=function(p1,p2,q1,q2){var p1q1p2=jsts.geom.Envelope.intersects(p1,p2,q1);var p1q2p2=jsts.geom.Envelope.intersects(p1,p2,q2);var q1p1q2=jsts.geom.Envelope.intersects(q1,q2,p1);var q1p2q2=jsts.geom.Envelope.intersects(q1,q2,p2);if(p1q1p2&&p1q2p2){this.intPt[0]=q1;this.intPt[1]=q2;return jsts.algorithm.LineIntersector.COLLINEAR_INTERSECTION;} +if(q1p1q2&&q1p2q2){this.intPt[0]=p1;this.intPt[1]=p2;return jsts.algorithm.LineIntersector.COLLINEAR_INTERSECTION;} +if(p1q1p2&&q1p1q2){this.intPt[0]=q1;this.intPt[1]=p1;return q1.equals(p1)&&!p1q2p2&&!q1p2q2?jsts.algorithm.LineIntersector.POINT_INTERSECTION:jsts.algorithm.LineIntersector.COLLINEAR_INTERSECTION;} +if(p1q1p2&&q1p2q2){this.intPt[0]=q1;this.intPt[1]=p2;return q1.equals(p2)&&!p1q2p2&&!q1p1q2?jsts.algorithm.LineIntersector.POINT_INTERSECTION:jsts.algorithm.LineIntersector.COLLINEAR_INTERSECTION;} +if(p1q2p2&&q1p1q2){this.intPt[0]=q2;this.intPt[1]=p1;return q2.equals(p1)&&!p1q1p2&&!q1p2q2?jsts.algorithm.LineIntersector.POINT_INTERSECTION:jsts.algorithm.LineIntersector.COLLINEAR_INTERSECTION;} +if(p1q2p2&&q1p2q2){this.intPt[0]=q2;this.intPt[1]=p2;return q2.equals(p2)&&!p1q1p2&&!q1p1q2?jsts.algorithm.LineIntersector.POINT_INTERSECTION:jsts.algorithm.LineIntersector.COLLINEAR_INTERSECTION;} +return jsts.algorithm.LineIntersector.NO_INTERSECTION;};jsts.algorithm.RobustLineIntersector.prototype.intersection=function(p1,p2,q1,q2){var intPt=this.intersectionWithNormalization(p1,p2,q1,q2);if(!this.isInSegmentEnvelopes(intPt)){intPt=jsts.algorithm.CentralEndpointIntersector.getIntersection(p1,p2,q1,q2);} +if(this.precisionModel!==null){this.precisionModel.makePrecise(intPt);} +return intPt;};jsts.algorithm.RobustLineIntersector.prototype.intersectionWithNormalization=function(p1,p2,q1,q2){var n1=new jsts.geom.Coordinate(p1);var n2=new jsts.geom.Coordinate(p2);var n3=new jsts.geom.Coordinate(q1);var n4=new jsts.geom.Coordinate(q2);var normPt=new jsts.geom.Coordinate();this.normalizeToEnvCentre(n1,n2,n3,n4,normPt);var intPt=this.safeHCoordinateIntersection(n1,n2,n3,n4);intPt.x+=normPt.x;intPt.y+=normPt.y;return intPt;};jsts.algorithm.RobustLineIntersector.prototype.safeHCoordinateIntersection=function(p1,p2,q1,q2){var intPt=null;try{intPt=jsts.algorithm.HCoordinate.intersection(p1,p2,q1,q2);}catch(e){if(e instanceof jsts.error.NotRepresentableError){intPt=jsts.algorithm.CentralEndpointIntersector.getIntersection(p1,p2,q1,q2);}else{throw e;}} +return intPt;};jsts.algorithm.RobustLineIntersector.prototype.normalizeToMinimum=function(n1,n2,n3,n4,normPt){normPt.x=this.smallestInAbsValue(n1.x,n2.x,n3.x,n4.x);normPt.y=this.smallestInAbsValue(n1.y,n2.y,n3.y,n4.y);n1.x-=normPt.x;n1.y-=normPt.y;n2.x-=normPt.x;n2.y-=normPt.y;n3.x-=normPt.x;n3.y-=normPt.y;n4.x-=normPt.x;n4.y-=normPt.y;};jsts.algorithm.RobustLineIntersector.prototype.normalizeToEnvCentre=function(n00,n01,n10,n11,normPt){var minX0=n00.xn01.x?n00.x:n01.x;var maxY0=n00.y>n01.y?n00.y:n01.y;var minX1=n10.xn11.x?n10.x:n11.x;var maxY1=n10.y>n11.y?n10.y:n11.y;var intMinX=minX0>minX1?minX0:minX1;var intMaxX=maxX0minY1?minY0:minY1;var intMaxY=maxY0=segStr.size()-2) +return true;return false;};jsts.noding.InteriorIntersectionFinder.prototype.isDone=function(){if(this.findAllIntersections) +return false;return this.interiorIntersection!=null;};})();(function(){jsts.noding.Noder=function(){};jsts.noding.Noder.prototype.computeNodes=jsts.abstractFunc;jsts.noding.Noder.prototype.getNodedSubstrings=jsts.abstractFunc;})();(function(){var Noder=jsts.noding.Noder;jsts.noding.SinglePassNoder=function(){};jsts.noding.SinglePassNoder.prototype=new Noder();jsts.noding.SinglePassNoder.constructor=jsts.noding.SinglePassNoder;jsts.noding.SinglePassNoder.prototype.segInt=null;jsts.noding.SinglePassNoder.prototype.setSegmentIntersector=function(segInt){this.segInt=segInt;};})();jsts.index.SpatialIndex=function(){};jsts.index.SpatialIndex.prototype.insert=function(itemEnv,item){throw new jsts.error.AbstractMethodInvocationError();};jsts.index.SpatialIndex.prototype.query=function(searchEnv,visitor){throw new jsts.error.AbstractMethodInvocationError();};jsts.index.SpatialIndex.prototype.remove=function(itemEnv,item){throw new jsts.error.AbstractMethodInvocationError();};jsts.index.strtree.AbstractSTRtree=function(nodeCapacity){if(nodeCapacity===undefined) +return;this.itemBoundables=[];jsts.util.Assert.isTrue(nodeCapacity>1,'Node capacity must be greater than 1');this.nodeCapacity=nodeCapacity;};jsts.index.strtree.AbstractSTRtree.IntersectsOp=function(){};jsts.index.strtree.AbstractSTRtree.IntersectsOp.prototype.intersects=function(aBounds,bBounds){throw new jsts.error.AbstractMethodInvocationError();};jsts.index.strtree.AbstractSTRtree.prototype.root=null;jsts.index.strtree.AbstractSTRtree.prototype.built=false;jsts.index.strtree.AbstractSTRtree.prototype.itemBoundables=null;jsts.index.strtree.AbstractSTRtree.prototype.nodeCapacity=null;jsts.index.strtree.AbstractSTRtree.prototype.build=function(){jsts.util.Assert.isTrue(!this.built);this.root=this.itemBoundables.length===0?this.createNode(0):this.createHigherLevels(this.itemBoundables,-1);this.built=true;};jsts.index.strtree.AbstractSTRtree.prototype.createNode=function(level){throw new jsts.error.AbstractMethodInvocationError();};jsts.index.strtree.AbstractSTRtree.prototype.createParentBoundables=function(childBoundables,newLevel){jsts.util.Assert.isTrue(!(childBoundables.length===0));var parentBoundables=[];parentBoundables.push(this.createNode(newLevel));var sortedChildBoundables=[];for(var i=0;ib?1:amaxChildDepth) +maxChildDepth=childDepth;}} +return maxChildDepth+1;};jsts.index.strtree.AbstractSTRtree.prototype.insert=function(bounds,item){jsts.util.Assert.isTrue(!this.built,'Cannot insert items into an STR packed R-tree after it has been built.');this.itemBoundables.push(new jsts.index.strtree.ItemBoundable(bounds,item));};jsts.index.strtree.AbstractSTRtree.prototype.query=function(searchBounds){if(arguments.length>1){this.query2.apply(this,arguments);} +if(!this.built){this.build();} +var matches=[];if(this.itemBoundables.length===0){jsts.util.Assert.isTrue(this.root.getBounds()===null);return matches;} +if(this.getIntersectsOp().intersects(this.root.getBounds(),searchBounds)){this.query3(searchBounds,this.root,matches);} +return matches;};jsts.index.strtree.AbstractSTRtree.prototype.query2=function(searchBounds,visitor){if(arguments.length>2){this.query3.apply(this,arguments);} +if(!this.built){this.build();} +if(this.itemBoundables.length===0){jsts.util.Assert.isTrue(this.root.getBounds()===null);} +if(this.getIntersectsOp().intersects(this.root.getBounds(),searchBounds)){this.query4(searchBounds,this.root,visitor);}};jsts.index.strtree.AbstractSTRtree.prototype.query3=function(searchBounds,node,matches){if(!(arguments[2]instanceof Array)){this.query4.apply(this,arguments);} +var childBoundables=node.getChildBoundables();for(var i=0;i1){this.boundablesAtLevel2.apply(this,arguments);return;} +var boundables=[];this.boundablesAtLevel2(level,this.root,boundables);return boundables;};jsts.index.strtree.AbstractSTRtree.prototype.boundablesAtLevel2=function(level,top,boundables){jsts.util.Assert.isTrue(level>-2);if(top.getLevel()===level){boundables.add(top);return;} +var childBoundables=node.getChildBoundables();for(var i=0;i0);var parentBoundables=[];for(var i=0;i0.0){var bndPair=priQ.pop();var currentDistance=bndPair.getDistance();if(currentDistance>=distanceLowerBound) +break;if(bndPair.isLeaves()){distanceLowerBound=currentDistance;minPair=bndPair;}else{bndPair.expandToQueue(priQ,distanceLowerBound);}} +return[minPair.getBoundable(0).getItem(),minPair.getBoundable(1).getItem()];};jsts.noding.SegmentString=function(){};jsts.noding.SegmentString.prototype.getData=jsts.abstractFunc;jsts.noding.SegmentString.prototype.setData=jsts.abstractFunc;jsts.noding.SegmentString.prototype.size=jsts.abstractFunc;jsts.noding.SegmentString.prototype.getCoordinate=jsts.abstractFunc;jsts.noding.SegmentString.prototype.getCoordinates=jsts.abstractFunc;jsts.noding.SegmentString.prototype.isClosed=jsts.abstractFunc;jsts.noding.NodableSegmentString=function(){};jsts.noding.NodableSegmentString.prototype=new jsts.noding.SegmentString();jsts.noding.NodableSegmentString.prototype.addIntersection=jsts.abstractFunc;jsts.noding.NodedSegmentString=function(pts,data){this.nodeList=new jsts.noding.SegmentNodeList(this);this.pts=pts;this.data=data;};jsts.noding.NodedSegmentString.prototype=new jsts.noding.NodableSegmentString();jsts.noding.NodedSegmentString.constructor=jsts.noding.NodedSegmentString;jsts.noding.NodedSegmentString.getNodedSubstrings=function(segStrings){if(arguments.length===2){jsts.noding.NodedSegmentString.getNodedSubstrings2.apply(this,arguments);return;} +var resultEdgelist=new javascript.util.ArrayList();jsts.noding.NodedSegmentString.getNodedSubstrings2(segStrings,resultEdgelist);return resultEdgelist;};jsts.noding.NodedSegmentString.getNodedSubstrings2=function(segStrings,resultEdgelist){for(var i=segStrings.iterator();i.hasNext();){var ss=i.next();ss.getNodeList().addSplitEdges(resultEdgelist);}};jsts.noding.NodedSegmentString.prototype.nodeList=null;jsts.noding.NodedSegmentString.prototype.pts=null;jsts.noding.NodedSegmentString.prototype.data=null;jsts.noding.NodedSegmentString.prototype.getData=function(){return this.data;};jsts.noding.NodedSegmentString.prototype.setData=function(data){this.data=data;};jsts.noding.NodedSegmentString.prototype.getNodeList=function(){return this.nodeList;};jsts.noding.NodedSegmentString.prototype.size=function(){return this.pts.length;};jsts.noding.NodedSegmentString.prototype.getCoordinate=function(i){return this.pts[i];};jsts.noding.NodedSegmentString.prototype.getCoordinates=function(){return this.pts;};jsts.noding.NodedSegmentString.prototype.isClosed=function(){return this.pts[0].equals(this.pts[this.pts.length-1]);};jsts.noding.NodedSegmentString.prototype.getSegmentOctant=function(index){if(index===this.pts.length-1) +return-1;return this.safeOctant(this.getCoordinate(index),this.getCoordinate(index+1));};jsts.noding.NodedSegmentString.prototype.safeOctant=function(p0,p1){if(p0.equals2D(p1)) +return 0;return jsts.noding.Octant.octant(p0,p1);};jsts.noding.NodedSegmentString.prototype.addIntersections=function(li,segmentIndex,geomIndex){for(var i=0;i=pts.length-1){return pts.length-1;} +var chainQuad=jsts.geomgraph.Quadrant.quadrant(pts[safeStart],pts[safeStart+1]);var last=start+1;while(last0&&factor<1){return this.project(p);} +var dist0=this.p0.distance(p);var dist1=this.p1.distance(p);if(dist0queryChain.getId()){queryChain.computeOverlaps(testChain,overlapAction);this.nOverlaps++;} +if(this.segInt.isDone()) +return;}}};jsts.noding.MCIndexNoder.prototype.add=function(segStr){var segChains=MonotoneChainBuilder.getChains(segStr.getCoordinates(),segStr);for(var i=0;i=0) +this.setComputationPrecision(g0.getPrecisionModel());else +this.setComputationPrecision(g1.getPrecisionModel());this.arg[0]=new jsts.geomgraph.GeometryGraph(0,g0,boundaryNodeRule);this.arg[1]=new jsts.geomgraph.GeometryGraph(1,g1,boundaryNodeRule);};jsts.operation.GeometryGraphOperation.prototype.li=null;jsts.operation.GeometryGraphOperation.prototype.resultPrecisionModel=null;jsts.operation.GeometryGraphOperation.prototype.arg=null;jsts.operation.GeometryGraphOperation.prototype.getArgGeometry=function(i){return arg[i].getGeometry();};jsts.operation.GeometryGraphOperation.prototype.setComputationPrecision=function(pm){this.resultPrecisionModel=pm;this.li.setPrecisionModel(this.resultPrecisionModel);};jsts.operation.overlay.PolygonBuilder=function(geometryFactory){this.shellList=[];this.geometryFactory=geometryFactory;};jsts.operation.overlay.PolygonBuilder.prototype.geometryFactory=null;jsts.operation.overlay.PolygonBuilder.prototype.shellList=null;jsts.operation.overlay.PolygonBuilder.prototype.add=function(graph){if(arguments.length===2){this.add2.apply(this,arguments);return;} +this.add2(graph.getEdgeEnds(),graph.getNodes());};jsts.operation.overlay.PolygonBuilder.prototype.add2=function(dirEdges,nodes){jsts.geomgraph.PlanarGraph.linkResultDirectedEdges(nodes);var maxEdgeRings=this.buildMaximalEdgeRings(dirEdges);var freeHoleList=[];var edgeRings=this.buildMinimalEdgeRings(maxEdgeRings,this.shellList,freeHoleList);this.sortShellsAndHoles(edgeRings,this.shellList,freeHoleList);this.placeFreeHoles(this.shellList,freeHoleList);};jsts.operation.overlay.PolygonBuilder.prototype.getPolygons=function(){var resultPolyList=this.computePolygons(this.shellList);return resultPolyList;};jsts.operation.overlay.PolygonBuilder.prototype.buildMaximalEdgeRings=function(dirEdges){var maxEdgeRings=[];for(var it=dirEdges.iterator();it.hasNext();){var de=it.next();if(de.isInResult()&&de.getLabel().isArea()){if(de.getEdgeRing()==null){var er=new jsts.operation.overlay.MaximalEdgeRing(de,this.geometryFactory);maxEdgeRings.push(er);er.setInResult();}}} +return maxEdgeRings;};jsts.operation.overlay.PolygonBuilder.prototype.buildMinimalEdgeRings=function(maxEdgeRings,shellList,freeHoleList){var edgeRings=[];for(var i=0;i2){er.linkDirectedEdgesForMinimalEdgeRings();var minEdgeRings=er.buildMinimalRings();var shell=this.findShell(minEdgeRings);if(shell!==null){this.placePolygonHoles(shell,minEdgeRings);shellList.push(shell);}else{freeHoleList=freeHoleList.concat(minEdgeRings);}}else{edgeRings.push(er);}} +return edgeRings;};jsts.operation.overlay.PolygonBuilder.prototype.findShell=function(minEdgeRings){var shellCount=0;var shell=null;for(var i=0;i=centre){subnodeIndex=1;} +if(interval.max<=centre){subnodeIndex=0;} +return subnodeIndex;};NodeBase.prototype.getItems=function(){return this.items;};NodeBase.prototype.add=function(item){this.items.add(item);};NodeBase.prototype.addAllItems=function(items){items.addAll(this.items);var i=0,il=2;for(i;imaxSubDepth){maxSubDepth=sqd;}}} +return maxSubDepth+1;};NodeBase.prototype.size=function(){var subSize=0,i=0,il=2;for(i;imax){this.min=max;this.max=min;}};Interval.prototype.getMin=function(){return this.min;};Interval.prototype.getMax=function(){return this.max;};Interval.prototype.getWidth=function(){return(this.max-this.min);};Interval.prototype.expandToInclude=function(interval){if(interval.max>this.max){this.max=interval.max;} +if(interval.minmax||this.max=this.min&&max<=this.max);};Interval.prototype.containsPoint=function(p){return(p>=this.min&&p<=this.max);};jsts.index.bintree.Interval=Interval;})();jsts.index.DoubleBits=function(){};jsts.index.DoubleBits.powerOf2=function(exp){return Math.pow(2,exp);};jsts.index.DoubleBits.exponent=function(d){return jsts.index.DoubleBits.CVTFWD(64,d)-1023;};jsts.index.DoubleBits.CVTFWD=function(NumW,Qty){var Sign,Expo,Mant,Bin,nb01='';var Inf={32:{d:0x7F,c:0x80,b:0,a:0},64:{d:0x7FF0,c:0,b:0,a:0}};var ExW={32:8,64:11}[NumW],MtW=NumW-ExW-1;if(!Bin){Sign=Qty<0||1/Qty<0;if(!isFinite(Qty)){Bin=Inf[NumW];if(Sign){Bin.d+=1<<(NumW/4-1);} +Expo=Math.pow(2,ExW)-1;Mant=0;}} +if(!Bin){Expo={32:127,64:1023}[NumW];Mant=Math.abs(Qty);while(Mant>=2){Expo++;Mant/=2;} +while(Mant<1&&Expo>0){Expo--;Mant*=2;} +if(Expo<=0){Mant/=2;nb01='Zero or Denormal';} +if(NumW===32&&Expo>254){nb01='Too big for Single';Bin={d:Sign?0xFF:0x7F,c:0x80,b:0,a:0};Expo=Math.pow(2,ExW)-1;Mant=0;}} +return Expo;};(function(){var DoubleBits=jsts.index.DoubleBits;var Interval=jsts.index.bintree.Interval;var Key=function(interval){this.pt=0.0;this.level=0;this.computeKey(interval);};Key.computeLevel=function(interval){var dx=interval.getWidth(),level;level=DoubleBits.exponent(dx)+1;return level;};Key.prototype.getPoint=function(){return this.pt;};Key.prototype.getLevel=function(){return this.level;};Key.prototype.getInterval=function(){return this.interval;};Key.prototype.computeKey=function(itemInterval){this.level=Key.computeLevel(itemInterval);this.interval=new Interval();this.computeInterval(this.level,itemInterval);while(!this.interval.contains(itemInterval)){this.level+=1;this.computeInterval(this.level,itemInterval);}};Key.prototype.computeInterval=function(level,itemInterval){var size=DoubleBits.powerOf2(level);this.pt=Math.floor(itemInterval.getMin()/size)*size;this.interval.init(this.pt,this.pt+size);};jsts.index.bintree.Key=Key;})();(function(){var NodeBase=jsts.index.bintree.NodeBase;var Key=jsts.index.bintree.Key;var Interval=jsts.index.bintree.Interval;var Node=function(interval,level){this.items=new javascript.util.ArrayList();this.subnode=[null,null];this.interval=interval;this.level=level;this.centre=(interval.getMin()+interval.getMax())/2;};Node.prototype=new NodeBase();Node.constructor=Node;Node.createNode=function(itemInterval){var key,node;key=new Key(itemInterval);node=new Node(key.getInterval(),key.getLevel());return node;};Node.createExpanded=function(node,addInterval){var expandInt,largerNode;expandInt=new Interval(addInterval);if(node!==null){expandInt.expandToInclude(node.interval);} +largerNode=Node.createNode(expandInt);if(node!==null){largerNode.insert(node);} +return largerNode;};Node.prototype.getInterval=function(){return this.interval;};Node.prototype.isSearchMatch=function(itemInterval){return itemInterval.overlaps(this.interval);};Node.prototype.getNode=function(searchInterval){var subnodeIndex=NodeBase.getSubnodeIndex(searchInterval,this.centre),node;if(subnodeIndex!=-1){node=this.getSubnode(subnodeIndex);return node.getNode(searchInterval);}else{return this;}};Node.prototype.find=function(searchInterval){var subnodeIndex=NodeBase.getSubnodeIndex(searchInterval,this.centre),node;if(subnodeIndex===-1){return this;} +if(this.subnode[subnodeIndex]!==null){node=this.subnode[subnodeIndex];return node.find(searchInterval);} +return this;};Node.prototype.insert=function(node){var index=NodeBase.getSubnodeIndex(node.interval,this.centre),childNode;if(node.level===this.level-1){this.subnode[index]=node;}else{childNode=this.createSubnode(index);childNode.insert(node);this.subnode[index]=childNode;}};Node.prototype.getSubnode=function(index){if(this.subnode[index]===null){this.subnode[index]=this.createSubnode(index);} +return this.subnode[index];};Node.prototype.createSubnode=function(index){var min,max,subInt,node;min=0.0;max=0.0;switch(index){case 0:min=this.interval.getMin();max=this.centre;break;case 1:min=this.centre;max=this.interval.getMax();break;} +subInt=new Interval(min,max);node=new Node(subInt,this.level-1);return node;};jsts.index.bintree.Node=Node;})();(function(){var Node=jsts.index.bintree.Node;var NodeBase=jsts.index.bintree.NodeBase;var Root=function(){this.subnode=[null,null];this.items=new javascript.util.ArrayList();};Root.prototype=new jsts.index.bintree.NodeBase();Root.constructor=Root;Root.origin=0.0;Root.prototype.insert=function(itemInterval,item){var index=NodeBase.getSubnodeIndex(itemInterval,Root.origin),node,largerNode;if(index===-1){this.add(item);return;} +node=this.subnode[index];if(node===null||!node.getInterval().contains(itemInterval)){largerNode=Node.createExpanded(node,itemInterval);this.subnode[index]=largerNode;} +this.insertContained(this.subnode[index],itemInterval,item);};Root.prototype.insertContained=function(tree,itemInterval,item){var isZeroArea,node;isZeroArea=jsts.index.IntervalSize.isZeroWidth(itemInterval.getMin(),itemInterval.getMax());node=isZeroArea?tree.find(itemInterval):tree.getNode(itemInterval);node.add(item);};Root.prototype.isSearchMatch=function(interval){return true;};jsts.index.bintree.Root=Root;})();(function(){var Interval=jsts.index.bintree.Interval;var Root=jsts.index.bintree.Root;var Bintree=function(){this.root=new Root();this.minExtent=1.0;};Bintree.ensureExtent=function(itemInterval,minExtent){var min,max;min=itemInterval.getMin();max=itemInterval.getMax();if(min!==max){return itemInterval;} +if(min===max){min=min-(minExtent/2.0);max=min+(minExtent/2.0);} +return new Interval(min,max);};Bintree.prototype.depth=function(){if(this.root!==null){return this.root.depth();} +return 0;};Bintree.prototype.size=function(){if(this.root!==null){return this.root.size();} +return 0;};Bintree.prototype.nodeSize=function(){if(this.root!==null){return this.root.nodeSize();} +return 0;};Bintree.prototype.insert=function(itemInterval,item){this.collectStats(itemInterval);var insertInterval=Bintree.ensureExtent(itemInterval,this.minExtent);this.root.insert(insertInterval,item);};Bintree.prototype.remove=function(itemInterval,item){var insertInterval=Bintree.ensureExtent(itemInterval,this.minExtent);return this.root.remove(insertInterval,item);};Bintree.prototype.iterator=function(){var foundItems=new javascript.util.ArrayList();this.root.addAllItems(foundItems);return foundItems.iterator();};Bintree.prototype.query=function(){if(arguments.length===2){this.queryAndAdd(arguments[0],arguments[1]);}else{var x=arguments[0];if(!x instanceof Interval){x=new Interval(x,x);} +return this.queryInterval(x);}};Bintree.prototype.queryInterval=function(interval){var foundItems=new javascript.util.ArrayList();this.query(interval,foundItems);return foundItems;};Bintree.prototype.queryAndAdd=function(interval,foundItems){this.root.addAllItemsFromOverlapping(interval,foundItems);};Bintree.prototype.collectStats=function(interval){var del=interval.getWidth();if(del0.0){this.minExtent=del;}};jsts.index.bintree.Bintree=Bintree;})();jsts.index.IntervalSize=function(){};jsts.index.IntervalSize.MIN_BINARY_EXPONENT=-50;jsts.index.IntervalSize.isZeroWidth=function(min,max){var width=max-min;if(width===0.0){return true;} +var maxAbs,scaledInterval,level;maxAbs=Math.max(Math.abs(min),Math.abs(max));scaledInterval=width/maxAbs;level=jsts.index.DoubleBits.exponent(scaledInterval);return level<=jsts.index.IntervalSize.MIN_BINARY_EXPONENT;};jsts.geomgraph.index.SimpleEdgeSetIntersector=function(){};jsts.geomgraph.index.SimpleEdgeSetIntersector.prototype=new jsts.geomgraph.index.EdgeSetIntersector();jsts.geomgraph.index.SimpleEdgeSetIntersector.prototype.nOverlaps=0;jsts.geomgraph.index.SimpleEdgeSetIntersector.prototype.computeIntersections=function(edges,si,testAllSegments){if(si instanceof javascript.util.List){this.computeIntersections2.apply(this,arguments);return;} +this.nOverlaps=0;for(var i0=edges.iterator();i0.hasNext();){var edge0=i0.next();for(var i1=edges.iterator();i1.hasNext();){var edge1=i1.next();if(testAllSegments||edge0!=edge1) +this.computeIntersects(edge0,edge1,si);}}};jsts.geomgraph.index.SimpleEdgeSetIntersector.prototype.computeIntersections2=function(edges0,edges1,si){this.nOverlaps=0;for(var i0=edges0.iterator();i0.hasNext();){var edge0=i0.next();for(var i1=edges1.iterator();i1.hasNext();){var edge1=i1.next();this.computeIntersects(edge0,edge1,si);}}};jsts.geomgraph.index.SimpleEdgeSetIntersector.prototype.computeIntersects=function(e0,e1,si){var pts0=e0.getCoordinates();var pts1=e1.getCoordinates();var i0,i1;for(i0=0;i00&&seqSize<4&&!this.preserveType) +return this.factory.createLineString(seq);return this.factory.createLinearRing(seq);};GeometryTransformer.prototype.transformLineString=function(geom,parent){return this.factory.createLineString(this.transformCoordinates(geom.getCoordinateSequence(),geom));};GeometryTransformer.prototype.transformMultiLineString=function(geom,parent){var transGeomList=new ArrayList();for(var i=0;isnapTolerance) +snapTolerance=fixedSnapTol;} +return snapTolerance;};GeometrySnapper.computeSizeBasedSnapTolerance=function(g){var env=g.getEnvelopeInternal();var minDimension=Math.min(env.getHeight(),env.getWidth());var snapTol=minDimension*GeometrySnapper.SNAP_PRECISION_FACTOR;return snapTol;};GeometrySnapper.computeOverlaySnapTolerance2=function(g0,g1){return Math.min(this.computeOverlaySnapTolerance(g0),this.computeOverlaySnapTolerance(g1));};GeometrySnapper.snap=function(g0,g1,snapTolerance){var snapGeom=[];var snapper0=new GeometrySnapper(g0);snapGeom[0]=snapper0.snapTo(g1,snapTolerance);var snapper1=new GeometrySnapper(g1);snapGeom[1]=snapper1.snapTo(snapGeom[0],snapTolerance);return snapGeom;};GeometrySnapper.snapToSelf=function(g0,snapTolerance,cleanResult){var snapper0=new GeometrySnapper(g0);return snapper0.snapToSelf(snapTolerance,cleanResult);};GeometrySnapper.prototype.srcGeom=null;GeometrySnapper.prototype.snapTo=function(snapGeom,snapTolerance){var snapPts=this.extractTargetCoordinates(snapGeom);var snapTrans=new SnapTransformer(snapTolerance,snapPts);return snapTrans.transform(this.srcGeom);};GeometrySnapper.prototype.snapToSelf=function(snapTolerance,cleanResult){var snapPts=this.extractTargetCoordinates(srcGeom);var snapTrans=new SnapTransformer(snapTolerance,snapPts,true);var snappedGeom=snapTrans.transform(srcGeom);var result=snappedGeom;if(cleanResult&&result instanceof Polygonal){result=snappedGeom.buffer(0);} +return result;};GeometrySnapper.prototype.extractTargetCoordinates=function(g){var ptSet=new TreeSet();var pts=g.getCoordinates();for(var i=0;i=0){if(dy>=0){if(adx>=ady) +return 0;else +return 1;} +else{if(adx>=ady) +return 7;else +return 6;}} +else{if(dy>=0){if(adx>=ady) +return 3;else +return 2;} +else{if(adx>=ady) +return 4;else +return 5;}}};jsts.noding.Octant.octant2=function(p0,p1){var dx=p1.x-p0.x;var dy=p1.y-p0.y;if(dx===0.0&&dy===0.0) +throw new jsts.error.IllegalArgumentError('Cannot compute the octant for two identical points '+p0);return jsts.noding.Octant.octant(dx,dy);};jsts.operation.union.UnionInteracting=function(g0,g1){this.g0=g0;this.g1=g1;this.geomFactory=g0.getFactory();this.interacts0=[];this.interacts1=[];};jsts.operation.union.UnionInteracting.union=function(g0,g1){var uue=new jsts.operation.union.UnionInteracting(g0,g1);return uue.union();};jsts.operation.union.UnionInteracting.prototype.geomFactory=null;jsts.operation.union.UnionInteracting.prototype.g0=null;jsts.operation.union.UnionInteracting.prototype.g1=null;jsts.operation.union.UnionInteracting.prototype.interacts0=null;jsts.operation.union.UnionInteracting.prototype.interacts1=null;jsts.operation.union.UnionInteracting.prototype.union=function(){this.computeInteracting();var int0=this.extractElements(this.g0,this.interacts0,true);var int1=this.extractElements(this.g1,this.interacts1,true);if(int0.isEmpty()||int1.isEmpty()){} +var union=in0.union(int1);var disjoint0=this.extractElements(this.g0,this.interacts0,false);var disjoint1=this.extractElements(this.g1,this.interacts1,false);var overallUnion=jsts.geom.util.GeometryCombiner.combine(union,disjoint0,disjoint1);return overallUnion;};jsts.operation.union.UnionInteracting.prototype.bufferUnion=function(g0,g1){var factory=g0.getFactory();var gColl=factory.createGeometryCollection([g0,g1]);var unionAll=gColl.buffer(0.0);return unionAll;};jsts.operation.union.UnionInteracting.prototype.computeInteracting=function(elem0){if(!elem0){for(var i=0,l=this.g0.getNumGeometries();i0)&&(y2<=0))||((y2>0)&&(y1<=0))){xInt=jsts.algorithm.RobustDeterminant.signOfDet2x2(x1,y1,x2,y2)/(y2-y1);if(0.0segMaxx||this.maxysegMaxy;if(isOutsidePixelEnv) +return false;var intersects=this.intersectsToleranceSquare(p0,p1);jsts.util.Assert.isTrue(!(isOutsidePixelEnv&&intersects),'Found bad envelope test');return intersects;};jsts.noding.snapround.HotPixel.prototype.intersectsToleranceSquare=function(p0,p1){var intersectsLeft=false;var intersectsBottom=false;this.li.computeIntersection(p0,p1,this.corner[0],this.corner[1]);if(this.li.isProper()) +return true;this.li.computeIntersection(p0,p1,this.corner[1],this.corner[2]);if(this.li.isProper()) +return true;if(this.li.hasIntersection()) +intersectsLeft=true;this.li.computeIntersection(p0,p1,this.corner[2],this.corner[3]);if(this.li.isProper()) +return true;if(this.li.hasIntersection()) +intersectsBottom=true;this.li.computeIntersection(p0,p1,this.corner[3],this.corner[0]);if(this.li.isProper()) +return true;if(intersectsLeft&&intersectsBottom) +return true;if(p0.equals(this.pt)) +return true;if(p1.equals(this.pt)) +return true;return false;};jsts.noding.snapround.HotPixel.prototype.intersectsPixelClosure=function(p0,p1){this.li.computeIntersection(p0,p1,this.corner[0],this.corner[1]);if(this.li.hasIntersection()) +return true;this.li.computeIntersection(p0,p1,this.corner[1],this.corner[2]);if(this.li.hasIntersection()) +return true;this.li.computeIntersection(p0,p1,this.corner[2],this.corner[3]);if(this.li.hasIntersection()) +return true;this.li.computeIntersection(p0,p1,this.corner[3],this.corner[0]);if(this.li.hasIntersection()) +return true;return false;};jsts.noding.snapround.HotPixel.prototype.addSnappedNode=function(segStr,segIndex){var p0=segStr.getCoordinate(segIndex);var p1=segStr.getCoordinate(segIndex+1);if(this.intersects(p0,p1)){segStr.addIntersection(this.getCoordinate(),segIndex);return true;} +return false;};jsts.operation.buffer.BufferInputLineSimplifier=function(inputLine){this.inputLine=inputLine;};jsts.operation.buffer.BufferInputLineSimplifier.simplify=function(inputLine,distanceTol){var simp=new jsts.operation.buffer.BufferInputLineSimplifier(inputLine);return simp.simplify(distanceTol);};jsts.operation.buffer.BufferInputLineSimplifier.INIT=0;jsts.operation.buffer.BufferInputLineSimplifier.DELETE=1;jsts.operation.buffer.BufferInputLineSimplifier.KEEP=1;jsts.operation.buffer.BufferInputLineSimplifier.prototype.inputLine=null;jsts.operation.buffer.BufferInputLineSimplifier.prototype.distanceTol=null;jsts.operation.buffer.BufferInputLineSimplifier.prototype.isDeleted=null;jsts.operation.buffer.BufferInputLineSimplifier.prototype.angleOrientation=jsts.algorithm.CGAlgorithms.COUNTERCLOCKWISE;jsts.operation.buffer.BufferInputLineSimplifier.prototype.simplify=function(distanceTol){this.distanceTol=Math.abs(distanceTol);if(distanceTol<0) +this.angleOrientation=jsts.algorithm.CGAlgorithms.CLOCKWISE;this.isDeleted=[];this.isDeleted.length=this.inputLine.length;var isChanged=false;do{isChanged=this.deleteShallowConcavities();}while(isChanged);return this.collapseLine();};jsts.operation.buffer.BufferInputLineSimplifier.prototype.deleteShallowConcavities=function(){var index=1;var maxIndex=this.inputLine.length-1;var midIndex=this.findNextNonDeletedIndex(index);var lastIndex=this.findNextNonDeletedIndex(midIndex);var isChanged=false;while(lastIndex1){return this.addCoordinates.apply(this,arguments);}else{return javascript.util.ArrayList.prototype.add.apply(this,arguments);}};jsts.geom.CoordinateList.prototype.addCoordinates=function(coord,allowRepeated,direction){if(coord instanceof jsts.geom.Coordinate){return this.addCoordinate.apply(this,arguments);}else if(typeof coord==='number'){return this.insertCoordinate.apply(this,arguments);} +direction=direction||true;if(direction){for(var i=0;i=0;i--){this.addCoordinate(coord[i],allowRepeated);}} +return true;};jsts.geom.CoordinateList.prototype.addCoordinate=function(coord,allowRepeated){if(!allowRepeated){if(this.size()>=1){var last=this.get(this.size()-1);if(last.equals2D(coord))return;}} +this.add(coord);};jsts.geom.CoordinateList.prototype.insertCoordinate=function(index,coord,allowRepeated){if(!allowRepeated){var before=index>0?index-1:-1;if(before!==-1&&this.get(before).equals2D(coord)){return;} +var after=index0){this.addCoordinate(new jsts.geom.Coordinate(this.get(0)),false);}};jsts.geom.CoordinateList.prototype.toArray=function(){var i,il,arr;i=0,il=this.size(),arr=[];for(i;i=iPrev) +pPrev=eiPrev.coord;var label=new jsts.geomgraph.Label(edge.getLabel());label.flip();var e=new jsts.geomgraph.EdgeEnd(edge,eiCurr.coord,pPrev,label);l.add(e);};jsts.operation.relate.EdgeEndBuilder.prototype.createEdgeEndForNext=function(edge,l,eiCurr,eiNext){var iNext=eiCurr.segmentIndex+1;if(iNext>=edge.getNumPoints()&&eiNext===null) +return;var pNext=edge.getCoordinate(iNext);if(eiNext!==null&&eiNext.segmentIndex===eiCurr.segmentIndex) +pNext=eiNext.coord;var e=new jsts.geomgraph.EdgeEnd(edge,eiCurr.coord,pNext,new jsts.geomgraph.Label(edge.getLabel()));l.add(e);};})();(function(){jsts.io.GeoJSONParser=function(geometryFactory){this.geometryFactory=geometryFactory||new jsts.geom.GeometryFactory();this.geometryTypes=['Point','MultiPoint','LineString','MultiLineString','Polygon','MultiPolygon'];};jsts.io.GeoJSONParser.prototype.read=function(json){var obj;if(typeof json==='string'){obj=JSON.parse(json);}else{obj=json;} +var type=obj.type;if(!this.parse[type]){throw new Error('Unknown GeoJSON type: '+obj.type);} +if(this.geometryTypes.indexOf(type)!=-1){return this.parse[type].apply(this,[obj.coordinates]);}else if(type==='GeometryCollection'){return this.parse[type].apply(this,[obj.geometries]);} +return this.parse[type].apply(this,[obj]);};jsts.io.GeoJSONParser.prototype.parse={'Feature':function(obj){var feature={};for(var key in obj){feature[key]=obj[key];} +if(obj.geometry){var type=obj.geometry.type;if(!this.parse[type]){throw new Error('Unknown GeoJSON type: '+obj.type);} +feature.geometry=this.read(obj.geometry);} +if(obj.bbox){feature.bbox=this.parse.bbox.apply(this,[obj.bbox]);} +return feature;},'FeatureCollection':function(obj){var featureCollection={};if(obj.features){featureCollection.features=[];for(var i=0;i0){return this.pts[0];}else{return null;}} +return this.pts[i];};jsts.geomgraph.Edge.prototype.isClosed=function(){return this.pts[0].equals(this.pts[this.pts.length-1]);};jsts.geomgraph.Edge.prototype.setIsolated=function(isIsolated){this._isIsolated=isIsolated;};jsts.geomgraph.Edge.prototype.isIsolated=function(){return this._isIsolated;};jsts.geomgraph.Edge.prototype.addIntersections=function(li,segmentIndex,geomIndex){for(var i=0;i=1){pt=ring.getCoordinateN(0);} +this.validErr=new jsts.operation.valid.TopologyValidationError(jsts.operation.valid.TopologyValidationError.RING_NOT_CLOSED,pt);}};jsts.operation.valid.IsValidOp.prototype.checkTooFewPoints=function(graph){if(graph.hasTooFewPoints){this.validErr=new jsts.operation.valid.TopologyValidationError(jsts.operation.valid.TopologyValidationError.TOO_FEW_POINTS,graph.getInvalidPoint());return;}};jsts.operation.valid.IsValidOp.prototype.checkConsistentArea=function(graph){var cat=new jsts.operation.valid.ConsistentAreaTester(graph);var isValidArea=cat.isNodeConsistentArea();if(!isValidArea){this.validErr=new jsts.operation.valid.TopologyValidationError(jsts.operation.valid.TopologyValidationError.SELF_INTERSECTION,cat.getInvalidPoint());return;} +if(cat.hasDuplicateRings()){this.validErr=new jsts.operation.valid.TopologyValidationError(jsts.operation.valid.TopologyValidationError.DUPLICATE_RINGS,cat.getInvalidPoint());}};jsts.operation.valid.IsValidOp.prototype.checkNoSelfIntersectingRings=function(graph){for(var i=graph.getEdgeIterator();i.hasNext();){var e=i.next();this.checkNoSelfIntersectingRing(e.getEdgeIntersectionList());if(this.validErr!=null){return;}}};jsts.operation.valid.IsValidOp.prototype.checkNoSelfIntersectingRing=function(eiList){var nodeSet=[];var isFirst=true;for(var i=eiList.iterator();i.hasNext();){var ei=i.next();if(isFirst){isFirst=false;continue;} +if(nodeSet.indexOf(ei.coord)>=0){this.validErr=new jsts.operation.valid.TopologyValidationError(jsts.operation.valid.TopologyValidationError.RING_SELF_INTERSECTION,ei.coord);return;}else{nodeSet.push(ei.coord);}}};jsts.operation.valid.IsValidOp.prototype.checkHolesInShell=function(p,graph){var shell=p.getExteriorRing();var pir=new jsts.algorithm.MCPointInRing(shell);for(var i=0;i0){if(x2>0){return-sign;} +else{return sign;}} +else{if(x2>0){return sign;} +else{return-sign;}}} +if((y1===0.0)||(x2===0.0)){if(y2>0){if(x1>0){return sign;} +else{return-sign;}} +else{if(x1>0){return-sign;} +else{return sign;}}} +if(0.0y2){sign=-sign;swap=x1;x1=x2;x2=swap;swap=y1;y1=y2;y2=swap;}} +else{if(y1<=-y2){sign=-sign;x2=-x2;y2=-y2;} +else{swap=x1;x1=-x2;x2=swap;swap=y1;y1=-y2;y2=swap;}}} +else{if(0.0=y2){x1=-x1;y1=-y1;x2=-x2;y2=-y2;} +else{sign=-sign;swap=-x1;x1=-x2;x2=swap;swap=-y1;y1=-y2;y2=swap;}}} +if(0.0x2){return sign;}} +else{return sign;}} +else{if(0.0=x2){sign=-sign;x1=-x1;x2=-x2;} +else{return-sign;}}} +while(true){count=count+1;k=Math.floor(x2/x1);x2=x2-k*x1;y2=y2-k*y1;if(y2<0.0){return-sign;} +if(y2>y1){return sign;} +if(x1>x2+x2){if(y1y2+y2){return-sign;} +else{x2=x1-x2;y2=y1-y2;sign=-sign;}} +if(y2===0.0){if(x2===0.0){return 0;} +else{return-sign;}} +if(x2===0.0){return sign;} +k=Math.floor(x1/x2);x1=x1-k*x2;y1=y1-k*y2;if(y1<0.0){return sign;} +if(y1>y2){return-sign;} +if(x2>x1+x1){if(y2y1+y1){return sign;} +else{x1=x2-x1;y1=y2-y1;sign=-sign;}} +if(y1===0.0){if(x1===0.0){return 0;} +else{return sign;}} +if(x1===0.0){return-sign;}}};jsts.algorithm.RobustDeterminant.orientationIndex=function(p1,p2,q){var dx1=p2.x-p1.x;var dy1=p2.y-p1.y;var dx2=q.x-p2.x;var dy2=q.y-p2.y;return jsts.algorithm.RobustDeterminant.signOfDet2x2(dx1,dy1,dx2,dy2);};jsts.index.quadtree.NodeBase=function(){this.subnode=new Array(4);this.subnode[0]=null;this.subnode[1]=null;this.subnode[2]=null;this.subnode[3]=null;this.items=[];};jsts.index.quadtree.NodeBase.prototype.getSubnodeIndex=function(env,centre){var subnodeIndex=-1;if(env.getMinX()>=centre.x){if(env.getMinY()>=centre.y){subnodeIndex=3;} +if(env.getMaxY()<=centre.y){subnodeIndex=1;}} +if(env.getMaxX()<=centre.x){if(env.getMinY()>=centre.y){subnodeIndex=2;} +if(env.getMaxY()<=centre.y){subnodeIndex=0;}} +return subnodeIndex;};jsts.index.quadtree.NodeBase.prototype.getItems=function(){return this.items;};jsts.index.quadtree.NodeBase.prototype.hasItems=function(){return(this.items.length>0);};jsts.index.quadtree.NodeBase.prototype.add=function(item){this.items.push(item);};jsts.index.quadtree.NodeBase.prototype.remove=function(itemEnv,item){if(!this.isSearchMatch(itemEnv)){return false;} +var found=false,i=0;for(i;i<4;i++){if(this.subnode[i]!==null){found=this.subnode[i].remove(itemEnv,item);if(found){if(this.subnode[i].isPrunable()){this.subnode[i]=null;} +break;}}} +if(found){return found;} +if(this.items.indexOf(item)!==-1){for(var i=this.items.length-1;i>=0;i--){if(this.items[i]===item){this.items.splice(i,1);}} +found=true;} +return found;};jsts.index.quadtree.NodeBase.prototype.isPrunable=function(){return!(this.hasChildren()||this.hasItems());};jsts.index.quadtree.NodeBase.prototype.hasChildren=function(){var i=0;for(i;i<4;i++){if(this.subnode[i]!==null){return true;}} +return false;};jsts.index.quadtree.NodeBase.prototype.isEmpty=function(){var isEmpty=true;if(this.items.length>0){isEmpty=false;} +var i=0;for(i;i<4;i++){if(this.subnode[i]!==null){if(!this.subnode[i].isEmpty()){isEmpty=false;}}} +return isEmpty;};jsts.index.quadtree.NodeBase.prototype.addAllItems=function(resultItems){resultItems=resultItems.concat(this.items);var i=0;for(i;i<4;i++){if(this.subnode[i]!==null){resultItems=this.subnode[i].addAllItems(resultItems);}} +return resultItems;};jsts.index.quadtree.NodeBase.prototype.addAllItemsFromOverlapping=function(searchEnv,resultItems){if(!this.isSearchMatch(searchEnv)){return;} +resultItems=resultItems.concat(this.items);var i=0;for(i;i<4;i++){if(this.subnode[i]!==null){resultItems=this.subnode[i].addAllItemsFromOverlapping(searchEnv,resultItems);}}};jsts.index.quadtree.NodeBase.prototype.visit=function(searchEnv,visitor){if(!this.isSearchMatch(searchEnv)){return;} +this.visitItems(searchEnv,visitor);var i=0;for(i;i<4;i++){if(this.subnode[i]!==null){this.subnode[i].visit(searchEnv,visitor);}}};jsts.index.quadtree.NodeBase.prototype.visitItems=function(env,visitor){var i=0,il=this.items.length;for(i;imaxSubDepth){maxSubDepth=sqd;}}} +return maxSubDepth+1;};jsts.index.quadtree.NodeBase.prototype.size=function(){var subSize=0,i=0;for(i;i<4;i++){if(this.subnode[i]!==null){subSize+=this.subnode[i].size();}} +return subSize+this.items.length;};jsts.index.quadtree.NodeBase.prototype.getNodeCount=function(){var subSize=0,i=0;for(i;i<4;i++){if(this.subnode[i]!==null){subSize+=this.subnode[i].size();}} +return subSize+1;};jsts.index.quadtree.Node=function(env,level){jsts.index.quadtree.NodeBase.prototype.constructor.apply(this,arguments);this.env=env;this.level=level;this.centre=new jsts.geom.Coordinate();this.centre.x=(env.getMinX()+env.getMaxX())/2;this.centre.y=(env.getMinY()+env.getMaxY())/2;};jsts.index.quadtree.Node.prototype=new jsts.index.quadtree.NodeBase();jsts.index.quadtree.Node.createNode=function(env){var key,node;key=new jsts.index.quadtree.Key(env);node=new jsts.index.quadtree.Node(key.getEnvelope(),key.getLevel());return node;};jsts.index.quadtree.Node.createExpanded=function(node,addEnv){var expandEnv=new jsts.geom.Envelope(addEnv),largerNode;if(node!==null){expandEnv.expandToInclude(node.env);} +largerNode=jsts.index.quadtree.Node.createNode(expandEnv);if(node!==null){largerNode.insertNode(node);} +return largerNode;};jsts.index.quadtree.Node.prototype.getEnvelope=function(){return this.env;};jsts.index.quadtree.Node.prototype.isSearchMatch=function(searchEnv){return this.env.intersects(searchEnv);};jsts.index.quadtree.Node.prototype.getNode=function(searchEnv){var subnodeIndex=this.getSubnodeIndex(searchEnv,this.centre),node;if(subnodeIndex!==-1){node=this.getSubnode(subnodeIndex);return node.getNode(searchEnv);}else{return this;}};jsts.index.quadtree.Node.prototype.find=function(searchEnv){var subnodeIndex=this.getSubnodeIndex(searchEnv,this.centre),node;if(subnodeIndex===-1){return this;} +if(this.subnode[subnodeIndex]!==null){node=this.subnode[subnodeIndex];return node.find(searchEnv);} +return this;};jsts.index.quadtree.Node.prototype.insertNode=function(node){var index=this.getSubnodeIndex(node.env,this.centre),childNode;if(node.level===this.level-1){this.subnode[index]=node;}else{childNode=this.createSubnode(index);childNode.insertNode(node);this.subnode[index]=childNode;}};jsts.index.quadtree.Node.prototype.getSubnode=function(index){if(this.subnode[index]===null){this.subnode[index]=this.createSubnode(index);} +return this.subnode[index];};jsts.index.quadtree.Node.prototype.createSubnode=function(index){var minx=0.0,maxx=0.0,miny=0.0,maxy=0.0,sqEnv,node;switch(index){case 0:minx=this.env.getMinX();maxx=this.centre.x;miny=this.env.getMinY();maxy=this.centre.y;break;case 1:minx=this.centre.x;maxx=this.env.getMaxX();miny=this.env.getMinY();maxy=this.centre.y;break;case 2:minx=this.env.getMinX();maxx=this.centre.x;miny=this.centre.y;maxy=this.env.getMaxY();break;case 3:minx=this.centre.x;maxx=this.env.getMaxX();miny=this.centre.y;maxy=this.env.getMaxY();break;} +sqEnv=new jsts.geom.Envelope(minx,maxx,miny,maxy);node=new jsts.index.quadtree.Node(sqEnv,this.level-1);return node;};(function(){jsts.triangulate.quadedge.QuadEdge=function(){this.rot=null;this.vertex=null;this.next=null;this.data=null;};var QuadEdge=jsts.triangulate.quadedge.QuadEdge;jsts.triangulate.quadedge.QuadEdge.makeEdge=function(o,d){var q0,q1,q2,q3,base;q0=new QuadEdge();q1=new QuadEdge();q2=new QuadEdge();q3=new QuadEdge();q0.rot=q1;q1.rot=q2;q2.rot=q3;q3.rot=q0;q0.setNext(q0);q1.setNext(q3);q2.setNext(q2);q3.setNext(q1);base=q0;base.setOrig(o);base.setDest(d);return base;};jsts.triangulate.quadedge.QuadEdge.connect=function(a,b){var e=QuadEdge.makeEdge(a.dest(),b.orig());QuadEdge.splice(e,a.lNext());QuadEdge.splice(e.sym(),b);return e;};jsts.triangulate.quadedge.QuadEdge.splice=function(a,b){var alpha,beta,t1,t2,t3,t4;alpha=a.oNext().rot;beta=b.oNext().rot;t1=b.oNext();t2=a.oNext();t3=beta.oNext();t4=alpha.oNext();a.setNext(t1);b.setNext(t2);alpha.setNext(t3);beta.setNext(t4);};jsts.triangulate.quadedge.QuadEdge.swap=function(e){var a,b;a=e.oPrev();b=e.sym().oPrev();QuadEdge.splice(e,a);QuadEdge.splice(e.sym(),b);QuadEdge.splice(e,a.lNext());QuadEdge.splice(e.sym(),b.lNext());e.setOrig(a.dest());e.setDest(b.dest());};jsts.triangulate.quadedge.QuadEdge.prototype.getPrimary=function(){if(this.orig().getCoordinate().compareTo(this.dest().getCoordinate())<=0){return this;} +else{return this.sym();}};jsts.triangulate.quadedge.QuadEdge.prototype.setData=function(data){this.data=data;};jsts.triangulate.quadedge.QuadEdge.prototype.getData=function(){return this.data;};jsts.triangulate.quadedge.QuadEdge.prototype.delete_jsts=function(){this.rot=null;};jsts.triangulate.quadedge.QuadEdge.prototype.isLive=function(){return this.rot!==null;};jsts.triangulate.quadedge.QuadEdge.prototype.setNext=function(next){this.next=next;};jsts.triangulate.quadedge.QuadEdge.prototype.invRot=function(){return this.rot.sym();};jsts.triangulate.quadedge.QuadEdge.prototype.sym=function(){return this.rot.rot;};jsts.triangulate.quadedge.QuadEdge.prototype.oNext=function(){return this.next;};jsts.triangulate.quadedge.QuadEdge.prototype.oPrev=function(){return this.rot.next.rot;};jsts.triangulate.quadedge.QuadEdge.prototype.dNext=function(){return this.sym().oNext().sym();};jsts.triangulate.quadedge.QuadEdge.prototype.dPrev=function(){return this.invRot().oNext().invRot();};jsts.triangulate.quadedge.QuadEdge.prototype.lNext=function(){return this.invRot().oNext().rot;};jsts.triangulate.quadedge.QuadEdge.prototype.lPrev=function(){return this.next.sym();};jsts.triangulate.quadedge.QuadEdge.prototype.rNext=function(){return this.rot.next.invRot();};jsts.triangulate.quadedge.QuadEdge.prototype.rPrev=function(){return this.sym().oNext();};jsts.triangulate.quadedge.QuadEdge.prototype.setOrig=function(o){this.vertex=o;};jsts.triangulate.quadedge.QuadEdge.prototype.setDest=function(d){this.sym().setOrig(d);};jsts.triangulate.quadedge.QuadEdge.prototype.orig=function(){return this.vertex;};jsts.triangulate.quadedge.QuadEdge.prototype.dest=function(){return this.sym().orig();};jsts.triangulate.quadedge.QuadEdge.prototype.getLength=function(){return this.orig().getCoordinate().distance(dest().getCoordinate());};jsts.triangulate.quadedge.QuadEdge.prototype.equalsNonOriented=function(qe){if(this.equalsOriented(qe)){return true;} +if(this.equalsOriented(qe.sym())){return true;} +return false;};jsts.triangulate.quadedge.QuadEdge.prototype.equalsOriented=function(qe){if(this.orig().getCoordinate().equals2D(qe.orig().getCoordinate())&&this.dest().getCoordinate().equals2D(qe.dest().getCoordinate())){return true;} +return false;};jsts.triangulate.quadedge.QuadEdge.prototype.toLineSegment=function() +{return new jsts.geom.LineSegment(this.vertex.getCoordinate(),this.dest().getCoordinate());};jsts.triangulate.quadedge.QuadEdge.prototype.toString=function(){var p0,p1;p0=this.vertex.getCoordinate();p1=this.dest().getCoordinate();return jsts.io.WKTWriter.toLineString(p0,p1);};})();(function(){var Assert=jsts.util.Assert;jsts.geomgraph.EdgeEnd=function(edge,p0,p1,label){this.edge=edge;if(p0&&p1){this.init(p0,p1);} +if(label){this.label=label||null;}};jsts.geomgraph.EdgeEnd.prototype.edge=null;jsts.geomgraph.EdgeEnd.prototype.label=null;jsts.geomgraph.EdgeEnd.prototype.node=null;jsts.geomgraph.EdgeEnd.prototype.p0=null;jsts.geomgraph.EdgeEnd.prototype.p1=null;jsts.geomgraph.EdgeEnd.prototype.dx=null;jsts.geomgraph.EdgeEnd.prototype.dy=null;jsts.geomgraph.EdgeEnd.prototype.quadrant=null;jsts.geomgraph.EdgeEnd.prototype.init=function(p0,p1){this.p0=p0;this.p1=p1;this.dx=p1.x-p0.x;this.dy=p1.y-p0.y;this.quadrant=jsts.geomgraph.Quadrant.quadrant(this.dx,this.dy);Assert.isTrue(!(this.dx===0&&this.dy===0),'EdgeEnd with identical endpoints found');};jsts.geomgraph.EdgeEnd.prototype.getEdge=function(){return this.edge;};jsts.geomgraph.EdgeEnd.prototype.getLabel=function(){return this.label;};jsts.geomgraph.EdgeEnd.prototype.getCoordinate=function(){return this.p0;};jsts.geomgraph.EdgeEnd.prototype.getDirectedCoordinate=function(){return this.p1;};jsts.geomgraph.EdgeEnd.prototype.getQuadrant=function(){return this.quadrant;};jsts.geomgraph.EdgeEnd.prototype.getDx=function(){return this.dx;};jsts.geomgraph.EdgeEnd.prototype.getDy=function(){return this.dy;};jsts.geomgraph.EdgeEnd.prototype.setNode=function(node){this.node=node;};jsts.geomgraph.EdgeEnd.prototype.getNode=function(){return this.node;};jsts.geomgraph.EdgeEnd.prototype.compareTo=function(e){return this.compareDirection(e);};jsts.geomgraph.EdgeEnd.prototype.compareDirection=function(e){if(this.dx===e.dx&&this.dy===e.dy) +return 0;if(this.quadrant>e.quadrant) +return 1;if(this.quadrant0;return isInCircle;};jsts.triangulate.quadedge.TrianglePredicate.isInCircleNormalized=function(a,b,c,p){var adx,ady,bdx,bdy,cdx,cdy,abdet,bcdet,cadet,alift,blift,clift,disc;adx=a.x-p.x;ady=a.y-p.y;bdx=b.x-p.x;bdy=b.y-p.y;cdx=c.x-p.x;cdy=c.y-p.y;abdet=adx*bdy-bdx*ady;bcdet=bdx*cdy-cdx*bdy;cadet=cdx*ady-adx*cdy;alift=adx*adx+ady*ady;blift=bdx*bdx+bdy*bdy;clift=cdx*cdx+cdy*cdy;disc=alift*bcdet+blift*cadet+clift*abdet;return disc>0;};jsts.triangulate.quadedge.TrianglePredicate.triArea=function(a,b,c){return(b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);};jsts.triangulate.quadedge.TrianglePredicate.isInCircleRobust=function(a,b,c,p){return jsts.triangulate.quadedge.TrianglePredicate.isInCircleNormalized(a,b,c,p);};jsts.triangulate.quadedge.TrianglePredicate.isInCircleDDSlow=function(a,b,c,p){var px,py,ax,ay,bx,by,cx,cy,aTerm,bTerm,cTerm,pTerm,sum,isInCircle;px=jsts.math.DD.valueOf(p.x);py=jsts.math.DD.valueOf(p.y);ax=jsts.math.DD.valueOf(a.x);ay=jsts.math.DD.valueOf(a.y);bx=jsts.math.DD.valueOf(b.x);by=jsts.math.DD.valueOf(b.y);cx=jsts.math.DD.valueOf(c.x);cy=jsts.math.DD.valueOf(c.y);aTerm=(ax.multiply(ax).add(ay.multiply(ay))).multiply(jsts.triangulate.quadedge.TrianglePredicate.triAreaDDSlow(bx,by,cx,cy,px,py));bTerm=(bx.multiply(bx).add(by.multiply(by))).multiply(jsts.triangulate.quadedge.TrianglePredicate.triAreaDDSlow(ax,ay,cx,cy,px,py));cTerm=(cx.multiply(cx).add(cy.multiply(cy))).multiply(jsts.triangulate.quadedge.TrianglePredicate.triAreaDDSlow(ax,ay,bx,by,px,py));pTerm=(px.multiply(px).add(py.multiply(py))).multiply(jsts.triangulate.quadedge.TrianglePredicate.triAreaDDSlow(ax,ay,bx,by,cx,cy));sum=aTerm.subtract(bTerm).add(cTerm).subtract(pTerm);isInCircle=sum.doubleValue()>0;return isInCircle;};jsts.triangulate.quadedge.TrianglePredicate.triAreaDDSlow=function(ax,ay,bx,by,cx,cy){return(bx.subtract(ax).multiply(cy.subtract(ay)).subtract(by.subtract(ay).multiply(cx.subtract(ax))));};jsts.triangulate.quadedge.TrianglePredicate.isInCircleDDFast=function(a,b,c,p){var aTerm,bTerm,cTerm,pTerm,sum,isInCircle;aTerm=(jsts.math.DD.sqr(a.x).selfAdd(jsts.math.DD.sqr(a.y))).selfMultiply(jsts.triangulate.quadedge.TrianglePredicate.triAreaDDFast(b,c,p));bTerm=(jsts.math.DD.sqr(b.x).selfAdd(jsts.math.DD.sqr(b.y))).selfMultiply(jsts.triangulate.quadedge.TrianglePredicate.triAreaDDFast(a,c,p));cTerm=(jsts.math.DD.sqr(c.x).selfAdd(jsts.math.DD.sqr(c.y))).selfMultiply(jsts.triangulate.quadedge.TrianglePredicate.triAreaDDFast(a,b,p));pTerm=(jsts.math.DD.sqr(p.x).selfAdd(jsts.math.DD.sqr(p.y))).selfMultiply(jsts.triangulate.quadedge.TrianglePredicate.triAreaDDFast(a,b,c));sum=aTerm.selfSubtract(bTerm).selfAdd(cTerm).selfSubtract(pTerm);isInCircle=sum.doubleValue()>0;return isInCircle;};jsts.triangulate.quadedge.TrianglePredicate.triAreaDDFast=function(a,b,c){var t1,t2;t1=jsts.math.DD.valueOf(b.x).selfSubtract(a.x).selfMultiply(jsts.math.DD.valueOf(c.y).selfSubtract(a.y));t2=jsts.math.DD.valueOf(b.y).selSubtract(a.y).selfMultiply(jsts.math.DD.valueOf(c.x).selfSubtract(a.x));return t1.selfSubtract(t2);};jsts.triangulate.quadedge.TrianglePredicate.isInCircleDDNormalized=function(a,b,c,p){var adx,ady,bdx,bdy,cdx,cdy,abdet,bcdet,cadet,alift,blift,clift,sum,isInCircle;adx=jsts.math.DD.valueOf(a.x).selfSubtract(p.x);ady=jsts.math.DD.valueOf(a.y).selfSubtract(p.y);bdx=jsts.math.DD.valueOf(b.x).selfSubtract(p.x);bdx=jsts.math.DD.valueOf(b.y).selfSubtract(p.y);cdx=jsts.math.DD.valueOf(c.x).selfSubtract(p.x);cdx=jsts.math.DD.valueOf(c.y).selfSubtract(p.y);abdet=adx.multiply(bdy).selfSubtract(bdx.multiply(ady));bcdet=bdx.multiply(cdy).selfSubtract(cdx.multiply(bdy));cadet=cdx.multiply(ady).selfSubtract(adx.multiply(cdy));alift=adx.multiply(adx).selfAdd(ady.multiply(ady));blift=bdx.multiply(bdx).selfAdd(bdy.multiply(bdy));clift=cdx.multiply(cdx).selfAdd(cdy.multiply(cdy));sum=alift.selfMultiply(bcdet).selfAdd(blift.selfMultiply(cadet)).selfAdd(clift.selfMultiply(abdet));isInCircle=sum.doubleValue()>0;return isInCircle;};jsts.triangulate.quadedge.TrianglePredicate.isInCircleCC=function(a,b,c,p){var cc,ccRadius,pRadiusDiff;cc=jsts.geom.Triangle.circumcentre(a,b,c);ccRadius=a.distance(cc);pRadiusDiff=p.distance(cc)-ccRadius;return pRadiusDiff<=0;};jsts.operation.buffer.RightmostEdgeFinder=function(){};jsts.operation.buffer.RightmostEdgeFinder.prototype.minIndex=-1;jsts.operation.buffer.RightmostEdgeFinder.prototype.minCoord=null;jsts.operation.buffer.RightmostEdgeFinder.prototype.minDe=null;jsts.operation.buffer.RightmostEdgeFinder.prototype.orientedDe=null;jsts.operation.buffer.RightmostEdgeFinder.prototype.getEdge=function(){return this.orientedDe;};jsts.operation.buffer.RightmostEdgeFinder.prototype.getCoordinate=function(){return this.minCoord;};jsts.operation.buffer.RightmostEdgeFinder.prototype.findEdge=function(dirEdgeList){for(var i=dirEdgeList.iterator();i.hasNext();){var de=i.next();if(!de.isForward()) +continue;this.checkForRightmostCoordinate(de);} +jsts.util.Assert.isTrue(this.minIndex!==0||this.minCoord.equals(this.minDe.getCoordinate()),'inconsistency in rightmost processing');if(this.minIndex===0){this.findRightmostEdgeAtNode();}else{this.findRightmostEdgeAtVertex();} +this.orientedDe=this.minDe;var rightmostSide=this.getRightmostSide(this.minDe,this.minIndex);if(rightmostSide==jsts.geomgraph.Position.LEFT){this.orientedDe=this.minDe.getSym();}};jsts.operation.buffer.RightmostEdgeFinder.prototype.findRightmostEdgeAtNode=function(){var node=this.minDe.getNode();var star=node.getEdges();this.minDe=star.getRightmostEdge();if(!this.minDe.isForward()){this.minDe=this.minDe.getSym();this.minIndex=this.minDe.getEdge().getCoordinates().length-1;}};jsts.operation.buffer.RightmostEdgeFinder.prototype.findRightmostEdgeAtVertex=function(){var pts=this.minDe.getEdge().getCoordinates();jsts.util.Assert.isTrue(this.minIndex>0&&this.minIndexthis.minCoord.y&&pNext.y>this.minCoord.y&&orientation===jsts.algorithm.CGAlgorithms.CLOCKWISE){usePrev=true;} +if(usePrev){this.minIndex=this.minIndex-1;}};jsts.operation.buffer.RightmostEdgeFinder.prototype.checkForRightmostCoordinate=function(de){var coord=de.getEdge().getCoordinates();for(var i=0;ithis.minCoord.x){this.minDe=de;this.minIndex=i;this.minCoord=coord[i];}}};jsts.operation.buffer.RightmostEdgeFinder.prototype.getRightmostSide=function(de,index){var side=this.getRightmostSideOfSegment(de,index);if(side<0) +side=this.getRightmostSideOfSegment(de,index-1);if(side<0){this.minCoord=null;this.checkForRightmostCoordinate(de);} +return side;};jsts.operation.buffer.RightmostEdgeFinder.prototype.getRightmostSideOfSegment=function(de,i){var e=de.getEdge();var coord=e.getCoordinates();if(i<0||i+1>=coord.length) +return-1;if(coord[i].y==coord[i+1].y) +return-1;var pos=jsts.geomgraph.Position.LEFT;if(coord[i].y0.0;};jsts.triangulate.IncrementalDelaunayTriangulator.prototype.insertSites=function(vertices){var i=0,il=vertices.length,v;for(i;i0.0){return jsts.triangulate.quadedge.Vertex.LEFT;} +if(sa<0.0){return jsts.triangulate.quadedge.Vertex.RIGHT;} +if((a.getX()*b.getX()<0.0)||(a.getY()*b.getY()<0.0)){return jsts.triangulate.quadedge.Vertex.BEHIND;} +if(a.magn()0);};jsts.triangulate.quadedge.Vertex.prototype.rightOf=function(e){return this.isCCW(e.dest(),e.orig());};jsts.triangulate.quadedge.Vertex.prototype.leftOf=function(e){return this.isCCW(e.orig(),e.dest());};jsts.triangulate.quadedge.Vertex.prototype.bisector=function(a,b){var dx,dy,l1,l2;dx=b.getX()-a.getX();dy=b.getY()-a.getY();l1=new jsts.algorithm.HCoordinate(a.getX()+(dx/2.0),a.getY()+ +(dy/2.0),1.0);l2=new jsts.algorithm.HCoordinate(a.getX()-dy+(dx/2.0),a.getY()+ +dx+(dy/2.0),1.0);return new jsts.algorithm.HCoordinate(l1,l2);};jsts.triangulate.quadedge.Vertex.prototype.distance=function(v1,v2){return v1.p.distance(v2.p);};jsts.triangulate.quadedge.Vertex.prototype.circumRadiusRatio=function(b,c){var x,radius,edgeLength,el;x=this.circleCenter(b,c);radius=this.distance(x,b);edgeLength=this.distance(this,b);el=this.distance(b,c);if(el=list.length){return null;} +return list[i];};jsts.operation.union.CascadedPolygonUnion.prototype.reduceToGeometries=function(geomTree){var geoms=[];for(var i=0,l=geomTree.length;i0){this.computeMinDistance2.apply(this,arguments);return;} +if(this.minDistanceLocation!==null) +return;this.minDistanceLocation=[];this.computeContainmentDistance();if(this.minDistance<=this.terminateDistance) +return;this.computeFacetDistance();};jsts.operation.distance.DistanceOp.prototype.computeContainmentDistance=function(){if(arguments.length===2){this.computeContainmentDistance2.apply(this,arguments);return;}else if(arguments.length===3&&(!arguments[0]instanceof jsts.operation.distance.GeometryLocation)){this.computeContainmentDistance3.apply(this,arguments);return;}else if(arguments.length===3){this.computeContainmentDistance4.apply(this,arguments);return;} +var locPtPoly=[];this.computeContainmentDistance2(0,locPtPoly);if(this.minDistance<=this.terminateDistance) +return;this.computeContainmentDistance2(1,locPtPoly);};jsts.operation.distance.DistanceOp.prototype.computeContainmentDistance2=function(polyGeomIndex,locPtPoly){var locationsIndex=1-polyGeomIndex;var polys=jsts.geom.util.PolygonExtracter.getPolygons(this.geom[polyGeomIndex]);if(polys.length>0){var insideLocs=jsts.operation.distance.ConnectedElementLocationFilter.getLocations(this.geom[locationsIndex]);this.computeContainmentDistance3(insideLocs,polys,locPtPoly);if(this.minDistance<=this.terminateDistance){this.minDistanceLocation[locationsIndex]=locPtPoly[0];this.minDistanceLocation[polyGeomIndex]=locPtPoly[1];return;}}};jsts.operation.distance.DistanceOp.prototype.computeContainmentDistance3=function(locs,polys,locPtPoly){for(var i=0;ithis.minDistance){return;} +var coord0=line0.getCoordinates();var coord1=line1.getCoordinates();for(var i=0;ithis.minDistance){return;} +var coord0=line.getCoordinates();var coord=pt.getCoordinate();for(var i=0;i=0.0){if(dy>=0.0) +return jsts.geomgraph.Quadrant.NE;else +return jsts.geomgraph.Quadrant.SE;}else{if(dy>=0.0) +return jsts.geomgraph.Quadrant.NW;else +return jsts.geomgraph.Quadrant.SW;}};jsts.geomgraph.Quadrant.quadrant2=function(p0,p1){if(p1.x===p0.x&&p1.y===p0.y) +throw new jsts.error.IllegalArgumentError('Cannot compute the quadrant for two identical points '+p0);if(p1.x>=p0.x){if(p1.y>=p0.y) +return jsts.geomgraph.Quadrant.NE;else +return jsts.geomgraph.Quadrant.SE;}else{if(p1.y>=p0.y) +return jsts.geomgraph.Quadrant.NW;else +return jsts.geomgraph.Quadrant.SW;}};jsts.geomgraph.Quadrant.isOpposite=function(quad1,quad2){if(quad1===quad2) +return false;var diff=(quad1-quad2+4)%4;if(diff===2) +return true;return false;};jsts.geomgraph.Quadrant.commonHalfPlane=function(quad1,quad2){if(quad1===quad2) +return quad1;var diff=(quad1-quad2+4)%4;if(diff===2) +return-1;var min=(quad1quad2)?quad1:quad2;if(min===0&&max===3) +return 3;return min;};jsts.geomgraph.Quadrant.isInHalfPlane=function(quad,halfPlane){if(halfPlane===jsts.geomgraph.Quadrant.SE){return quad===jsts.geomgraph.Quadrant.SE||quad===jsts.geomgraph.Quadrant.SW;} +return quad===halfPlane||quad===halfPlane+1;};jsts.geomgraph.Quadrant.isNorthern=function(quad){return quad===jsts.geomgraph.Quadrant.NE||quad===jsts.geomgraph.Quadrant.NW;};jsts.operation.valid.ConsistentAreaTester=function(geomGraph){this.geomGraph=geomGraph;this.li=new jsts.algorithm.RobustLineIntersector();this.nodeGraph=new jsts.operation.relate.RelateNodeGraph();this.invalidPoint=null;};jsts.operation.valid.ConsistentAreaTester.prototype.getInvalidPoint=function(){return this.invalidPoint;};jsts.operation.valid.ConsistentAreaTester.prototype.isNodeConsistentArea=function(){var intersector=this.geomGraph.computeSelfNodes(this.li,true);if(intersector.hasProperIntersection()){this.invalidPoint=intersector.getProperIntersectionPoint();return false;} +this.nodeGraph.build(this.geomGraph);return this.isNodeEdgeAreaLabelsConsistent();};jsts.operation.valid.ConsistentAreaTester.prototype.isNodeEdgeAreaLabelsConsistent=function(){for(var nodeIt=this.nodeGraph.getNodeIterator();nodeIt.hasNext();){var node=nodeIt.next();if(!node.getEdges().isAreaLabelsConsistent(this.geomGraph)){this.invalidPoint=node.getCoordinate().clone();return false;}} +return true;};jsts.operation.valid.ConsistentAreaTester.prototype.hasDuplicateRings=function(){for(var nodeIt=this.nodeGraph.getNodeIterator();nodeIt.hasNext();){var node=nodeIt.next();for(var i=node.getEdges().iterator();i.hasNext();){var eeb=i.next();if(eeb.getEdgeEnds().length>1){invalidPoint=eeb.getEdge().getCoordinate(0);return true;}}} +return false;};jsts.index.strtree.AbstractNode=function(level){this.level=level;this.childBoundables=[];};jsts.index.strtree.AbstractNode.prototype=new jsts.index.strtree.Boundable();jsts.index.strtree.AbstractNode.constructor=jsts.index.strtree.AbstractNode;jsts.index.strtree.AbstractNode.prototype.childBoundables=null;jsts.index.strtree.AbstractNode.prototype.bounds=null;jsts.index.strtree.AbstractNode.prototype.level=null;jsts.index.strtree.AbstractNode.prototype.getChildBoundables=function(){return this.childBoundables;};jsts.index.strtree.AbstractNode.prototype.computeBounds=function(){throw new jsts.error.AbstractMethodInvocationError();};jsts.index.strtree.AbstractNode.prototype.getBounds=function(){if(this.bounds===null){this.bounds=this.computeBounds();} +return this.bounds;};jsts.index.strtree.AbstractNode.prototype.getLevel=function(){return this.level;};jsts.index.strtree.AbstractNode.prototype.addChildBoundable=function(childBoundable){this.childBoundables.push(childBoundable);};(function(){var Location=jsts.geom.Location;var Position=jsts.geomgraph.Position;var EdgeEnd=jsts.geomgraph.EdgeEnd;jsts.geomgraph.DirectedEdge=function(edge,isForward){EdgeEnd.call(this,edge);this.depth=[0,-999,-999];this._isForward=isForward;if(isForward){this.init(edge.getCoordinate(0),edge.getCoordinate(1));}else{var n=edge.getNumPoints()-1;this.init(edge.getCoordinate(n),edge.getCoordinate(n-1));} +this.computeDirectedLabel();};jsts.geomgraph.DirectedEdge.prototype=new EdgeEnd();jsts.geomgraph.DirectedEdge.constructor=jsts.geomgraph.DirectedEdge;jsts.geomgraph.DirectedEdge.depthFactor=function(currLocation,nextLocation){if(currLocation===Location.EXTERIOR&&nextLocation===Location.INTERIOR) +return 1;else if(currLocation===Location.INTERIOR&&nextLocation===Location.EXTERIOR) +return-1;return 0;};jsts.geomgraph.DirectedEdge.prototype._isForward=null;jsts.geomgraph.DirectedEdge.prototype._isInResult=false;jsts.geomgraph.DirectedEdge.prototype._isVisited=false;jsts.geomgraph.DirectedEdge.prototype.sym=null;jsts.geomgraph.DirectedEdge.prototype.next=null;jsts.geomgraph.DirectedEdge.prototype.nextMin=null;jsts.geomgraph.DirectedEdge.prototype.edgeRing=null;jsts.geomgraph.DirectedEdge.prototype.minEdgeRing=null;jsts.geomgraph.DirectedEdge.prototype.depth=null;jsts.geomgraph.DirectedEdge.prototype.getEdge=function(){return this.edge;};jsts.geomgraph.DirectedEdge.prototype.setInResult=function(isInResult){this._isInResult=isInResult;};jsts.geomgraph.DirectedEdge.prototype.isInResult=function(){return this._isInResult;};jsts.geomgraph.DirectedEdge.prototype.isVisited=function(){return this._isVisited;};jsts.geomgraph.DirectedEdge.prototype.setVisited=function(isVisited){this._isVisited=isVisited;};jsts.geomgraph.DirectedEdge.prototype.setEdgeRing=function(edgeRing){this.edgeRing=edgeRing;};jsts.geomgraph.DirectedEdge.prototype.getEdgeRing=function(){return this.edgeRing;};jsts.geomgraph.DirectedEdge.prototype.setMinEdgeRing=function(minEdgeRing){this.minEdgeRing=minEdgeRing;};jsts.geomgraph.DirectedEdge.prototype.getMinEdgeRing=function(){return this.minEdgeRing;};jsts.geomgraph.DirectedEdge.prototype.getDepth=function(position){return this.depth[position];};jsts.geomgraph.DirectedEdge.prototype.setDepth=function(position,depthVal){if(this.depth[position]!==-999){if(this.depth[position]!==depthVal) +throw new jsts.error.TopologyError('assigned depths do not match',this.getCoordinate());} +this.depth[position]=depthVal;};jsts.geomgraph.DirectedEdge.prototype.getDepthDelta=function(){var depthDelta=this.edge.getDepthDelta();if(!this._isForward) +depthDelta=-depthDelta;return depthDelta;};jsts.geomgraph.DirectedEdge.prototype.setVisitedEdge=function(isVisited){this.setVisited(isVisited);this.sym.setVisited(isVisited);};jsts.geomgraph.DirectedEdge.prototype.getSym=function(){return this.sym;};jsts.geomgraph.DirectedEdge.prototype.isForward=function(){return this._isForward;};jsts.geomgraph.DirectedEdge.prototype.setSym=function(de){this.sym=de;};jsts.geomgraph.DirectedEdge.prototype.getNext=function(){return this.next;};jsts.geomgraph.DirectedEdge.prototype.setNext=function(next){this.next=next;};jsts.geomgraph.DirectedEdge.prototype.getNextMin=function(){return this.nextMin;};jsts.geomgraph.DirectedEdge.prototype.setNextMin=function(nextMin){this.nextMin=nextMin;};jsts.geomgraph.DirectedEdge.prototype.isLineEdge=function(){var isLine=this.label.isLine(0)||this.label.isLine(1);var isExteriorIfArea0=!this.label.isArea(0)||this.label.allPositionsEqual(0,Location.EXTERIOR);var isExteriorIfArea1=!this.label.isArea(1)||this.label.allPositionsEqual(1,Location.EXTERIOR);return isLine&&isExteriorIfArea0&&isExteriorIfArea1;};jsts.geomgraph.DirectedEdge.prototype.isInteriorAreaEdge=function(){var isInteriorAreaEdge=true;for(var i=0;i<2;i++){if(!(this.label.isArea(i)&&this.label.getLocation(i,Position.LEFT)===Location.INTERIOR&&this.label.getLocation(i,Position.RIGHT)===Location.INTERIOR)){isInteriorAreaEdge=false;}} +return isInteriorAreaEdge;};jsts.geomgraph.DirectedEdge.prototype.computeDirectedLabel=function(){this.label=new jsts.geomgraph.Label(this.edge.getLabel());if(!this._isForward) +this.label.flip();};jsts.geomgraph.DirectedEdge.prototype.setEdgeDepths=function(position,depth){var depthDelta=this.getEdge().getDepthDelta();if(!this._isForward) +depthDelta=-depthDelta;var directionFactor=1;if(position===Position.LEFT) +directionFactor=-1;var oppositePos=Position.opposite(position);var delta=depthDelta*directionFactor;var oppositeDepth=depth+delta;this.setDepth(position,depth);this.setDepth(oppositePos,oppositeDepth);};})();jsts.operation.buffer.OffsetCurveBuilder=function(precisionModel,bufParams){this.precisionModel=precisionModel;this.bufParams=bufParams;};jsts.operation.buffer.OffsetCurveBuilder.prototype.distance=0.0;jsts.operation.buffer.OffsetCurveBuilder.prototype.precisionModel=null;jsts.operation.buffer.OffsetCurveBuilder.prototype.bufParams=null;jsts.operation.buffer.OffsetCurveBuilder.prototype.getBufferParameters=function(){return this.bufParams;};jsts.operation.buffer.OffsetCurveBuilder.prototype.getLineCurve=function(inputPts,distance){this.distance=distance;if(this.distance<0.0&&!this.bufParams.isSingleSided()) +return null;if(this.distance==0.0) +return null;var posDistance=Math.abs(this.distance);var segGen=this.getSegGen(posDistance);if(inputPts.length<=1){this.computePointCurve(inputPts[0],segGen);}else{if(this.bufParams.isSingleSided()){var isRightSide=distance<0.0;this.computeSingleSidedBufferCurve(inputPts,isRightSide,segGen);}else +this.computeLineBufferCurve(inputPts,segGen);} +var lineCoord=segGen.getCoordinates();return lineCoord;};jsts.operation.buffer.OffsetCurveBuilder.prototype.getRingCurve=function(inputPts,side,distance){this.distance=distance;if(inputPts.length<=2) +return this.getLineCurve(inputPts,distance);if(this.distance==0.0){return jsts.operation.buffer.OffsetCurveBuilder.copyCoordinates(inputPts);} +var segGen=this.getSegGen(this.distance);this.computeRingBufferCurve(inputPts,side,segGen);return segGen.getCoordinates();};jsts.operation.buffer.OffsetCurveBuilder.prototype.getOffsetCurve=function(inputPts,distance){this.distance=distance;if(this.distance===0.0) +return null;var isRightSide=this.distance<0.0;var posDistance=Math.abs(this.distance);var segGen=this.getSegGen(posDistance);if(inputPts.length<=1){this.computePointCurve(inputPts[0],segGen);}else{this.computeOffsetCurve(inputPts,isRightSide,segGen);} +var curvePts=segGen.getCoordinates();if(isRightSide) +curvePts.reverse();return curvePts;};jsts.operation.buffer.OffsetCurveBuilder.copyCoordinates=function(pts){var copy=[];for(var i=0;i=0;i--){segGen.addNextSegment(simp2[i],true);} +segGen.addLastSegment();segGen.addLineEndCap(simp2[1],simp2[0]);segGen.closeRing();};jsts.operation.buffer.OffsetCurveBuilder.prototype.computeSingleSidedBufferCurve=function(inputPts,isRightSide,segGen){var distTol=jsts.operation.buffer.OffsetCurveBuilder.simplifyTolerance(this.distance);if(isRightSide){segGen.addSegments(inputPts,true);var simp2=jsts.operation.buffer.BufferInputLineSimplifier.simplify(inputPts,-distTol);var n2=simp2.length-1;segGen.initSideSegments(simp2[n2],simp2[n2-1],jsts.geomgraph.Position.LEFT);segGen.addFirstSegment();for(var i=n2-2;i>=0;i--){segGen.addNextSegment(simp2[i],true);}}else{segGen.addSegments(inputPts,false);var simp1=jsts.operation.buffer.BufferInputLineSimplifier.simplify(inputPts,distTol);var n1=simp1.length-1;segGen.initSideSegments(simp1[0],simp1[1],jsts.geomgraph.Position.LEFT);segGen.addFirstSegment();for(var i=2;i<=n1;i++){segGen.addNextSegment(simp1[i],true);}} +segGen.addLastSegment();segGen.closeRing();};jsts.operation.buffer.OffsetCurveBuilder.prototype.computeOffsetCurve=function(inputPts,isRightSide,segGen){var distTol=jsts.operation.buffer.OffsetCurveBuilder.simplifyTolerance(this.distance);if(isRightSide){var simp2=jsts.operation.buffer.BufferInputLineSimplifier.simplify(inputPts,-distTol);var n2=simp2.length-1;segGen.initSideSegments(simp2[n2],simp2[n2-1],jsts.geomgraph.Position.LEFT);segGen.addFirstSegment();for(var i=n2-2;i>=0;i--){segGen.addNextSegment(simp2[i],true);}}else{var simp1=jsts.operation.buffer.BufferInputLineSimplifier.simplify(inputPts,distTol);var n1=simp1.length-1;segGen.initSideSegments(simp1[0],simp1[1],jsts.geomgraph.Position.LEFT);segGen.addFirstSegment();for(var i=2;i<=n1;i++){segGen.addNextSegment(simp1[i],true);}} +segGen.addLastSegment();};jsts.operation.buffer.OffsetCurveBuilder.prototype.computeRingBufferCurve=function(inputPts,side,segGen){var distTol=jsts.operation.buffer.OffsetCurveBuilder.simplifyTolerance(this.distance);if(side===jsts.geomgraph.Position.RIGHT) +distTol=-distTol;var simp=jsts.operation.buffer.BufferInputLineSimplifier.simplify(inputPts,distTol);var n=simp.length-1;segGen.initSideSegments(simp[n-1],simp[0],side);for(var i=1;i<=n;i++){var addStartPoint=i!==1;segGen.addNextSegment(simp[i],addStartPoint);} +segGen.closeRing();};jsts.index.strtree.SIRtree=function(nodeCapacity){nodeCapacity=nodeCapacity||10;jsts.index.strtree.AbstractSTRtree.call(this,nodeCapacity);};jsts.index.strtree.SIRtree.prototype=new jsts.index.strtree.AbstractSTRtree();jsts.index.strtree.SIRtree.constructor=jsts.index.strtree.SIRtree;jsts.index.strtree.SIRtree.prototype.comperator={compare:function(o1,o2){return o1.getBounds().getCentre()-o2.getBounds().getCentre();}};jsts.index.strtree.SIRtree.prototype.intersectionOp={intersects:function(aBounds,bBounds){return aBounds.intersects(bBounds);}};jsts.index.strtree.SIRtree.prototype.createNode=function(level){var AbstractNode=function(level){jsts.index.strtree.AbstractNode.apply(this,arguments);};AbstractNode.prototype=new jsts.index.strtree.AbstractNode();AbstractNode.constructor=AbstractNode;AbstractNode.prototype.computeBounds=function(){var bounds=null,childBoundables=this.getChildBoundables(),childBoundable;for(var i=0,l=childBoundables.length;i0){return this.isNull2.apply(this,arguments);} +for(var i=0;i<2;i++){for(var j=0;j<3;j++){if(this.depth[i][j]!==jsts.geomgraph.Depth.NULL_VALUE) +return false;}} +return true;};jsts.geomgraph.Depth.prototype.isNull2=function(geomIndex){if(arguments.length>1){return this.isNull3.apply(this,arguments);} +return this.depth[geomIndex][1]==jsts.geomgraph.Depth.NULL_VALUE;};jsts.geomgraph.Depth.prototype.isNull3=function(geomIndex,posIndex){return this.depth[geomIndex][posIndex]==jsts.geomgraph.Depth.NULL_VALUE;};jsts.geomgraph.Depth.prototype.add=function(lbl){for(var i=0;i<2;i++){for(var j=1;j<3;j++){var loc=lbl.getLocation(i,j);if(loc===Location.EXTERIOR||loc===Location.INTERIOR){if(this.isNull(i,j)){this.depth[i][j]=jsts.geomgraph.Depth.depthAtLocation(loc);}else +this.depth[i][j]+=jsts.geomgraph.Depth.depthAtLocation(loc);}}}};jsts.geomgraph.Depth.prototype.getDelta=function(geomIndex){return this.depth[geomIndex][Position.RIGHT]- +this.depth[geomIndex][Position.LEFT];};jsts.geomgraph.Depth.prototype.normalize=function(){for(var i=0;i<2;i++){if(!this.isNull(i)){var minDepth=this.depth[i][1];if(this.depth[i][2]minDepth) +newValue=1;this.depth[i][j]=newValue;}}}};jsts.geomgraph.Depth.prototype.toString=function(){return'A: '+this.depth[0][1]+','+this.depth[0][2]+' B: '+ +this.depth[1][1]+','+this.depth[1][2];};})();jsts.operation.buffer.BufferParameters=function(quadrantSegments,endCapStyle,joinStyle,mitreLimit){if(quadrantSegments) +this.setQuadrantSegments(quadrantSegments);if(endCapStyle) +this.setEndCapStyle(endCapStyle);if(joinStyle) +this.setJoinStyle(joinStyle);if(mitreLimit) +this.setMitreLimit(mitreLimit);};jsts.operation.buffer.BufferParameters.CAP_ROUND=1;jsts.operation.buffer.BufferParameters.CAP_FLAT=2;jsts.operation.buffer.BufferParameters.CAP_SQUARE=3;jsts.operation.buffer.BufferParameters.JOIN_ROUND=1;jsts.operation.buffer.BufferParameters.JOIN_MITRE=2;jsts.operation.buffer.BufferParameters.JOIN_BEVEL=3;jsts.operation.buffer.BufferParameters.DEFAULT_QUADRANT_SEGMENTS=8;jsts.operation.buffer.BufferParameters.DEFAULT_MITRE_LIMIT=5.0;jsts.operation.buffer.BufferParameters.prototype.quadrantSegments=jsts.operation.buffer.BufferParameters.DEFAULT_QUADRANT_SEGMENTS;jsts.operation.buffer.BufferParameters.prototype.endCapStyle=jsts.operation.buffer.BufferParameters.CAP_ROUND;jsts.operation.buffer.BufferParameters.prototype.joinStyle=jsts.operation.buffer.BufferParameters.JOIN_ROUND;jsts.operation.buffer.BufferParameters.prototype.mitreLimit=jsts.operation.buffer.BufferParameters.DEFAULT_MITRE_LIMIT;jsts.operation.buffer.BufferParameters.prototype._isSingleSided=false;jsts.operation.buffer.BufferParameters.prototype.getQuadrantSegments=function(){return this.quadrantSegments;};jsts.operation.buffer.BufferParameters.prototype.setQuadrantSegments=function(quadrantSegments){this.quadrantSegments=quadrantSegments;};jsts.operation.buffer.BufferParameters.prototype.setQuadrantSegments=function(quadSegs){this.quadrantSegments=quadSegs;if(this.quadrantSegments===0) +this.joinStyle=jsts.operation.buffer.BufferParameters.JOIN_BEVEL;if(this.quadrantSegments<0){this.joinStyle=jsts.operation.buffer.BufferParameters.JOIN_MITRE;this.mitreLimit=Math.abs(this.quadrantSegments);} +if(quadSegs<=0){this.quadrantSegments=1;} +if(this.joinStyle!==jsts.operation.buffer.BufferParameters.JOIN_ROUND){this.quadrantSegments=jsts.operation.buffer.BufferParameters.DEFAULT_QUADRANT_SEGMENTS;}};jsts.operation.buffer.BufferParameters.bufferDistanceError=function(quadSegs){var alpha=Math.PI/2.0/quadSegs;return 1-Math.cos(alpha/2.0);};jsts.operation.buffer.BufferParameters.prototype.getEndCapStyle=function(){return this.endCapStyle;};jsts.operation.buffer.BufferParameters.prototype.setEndCapStyle=function(endCapStyle){this.endCapStyle=endCapStyle;};jsts.operation.buffer.BufferParameters.prototype.getJoinStyle=function(){return this.joinStyle;};jsts.operation.buffer.BufferParameters.prototype.setJoinStyle=function(joinStyle){this.joinStyle=joinStyle;};jsts.operation.buffer.BufferParameters.prototype.getMitreLimit=function(){return this.mitreLimit;};jsts.operation.buffer.BufferParameters.prototype.setMitreLimit=function(mitreLimit){this.mitreLimit=mitreLimit;};jsts.operation.buffer.BufferParameters.prototype.setSingleSided=function(isSingleSided){this._isSingleSided=isSingleSided;};jsts.operation.buffer.BufferParameters.prototype.isSingleSided=function(){return this._isSingleSided;};jsts.algorithm.distance.PointPairDistance=function(){this.pt=[new jsts.geom.Coordinate(),new jsts.geom.Coordinate()];};jsts.algorithm.distance.PointPairDistance.prototype.pt=null;jsts.algorithm.distance.PointPairDistance.prototype.distance=NaN;jsts.algorithm.distance.PointPairDistance.prototype.isNull=true;jsts.algorithm.distance.PointPairDistance.prototype.initialize=function(p0,p1,distance){if(p0===undefined){this.isNull=true;return;} +this.pt[0].setCoordinate(p0);this.pt[1].setCoordinate(p1);this.distance=distance!==undefined?distance:p0.distance(p1);this.isNull=false;};jsts.algorithm.distance.PointPairDistance.prototype.getDistance=function(){return this.distance;};jsts.algorithm.distance.PointPairDistance.prototype.getCoordinates=function(){return this.pt;};jsts.algorithm.distance.PointPairDistance.prototype.getCoordinate=function(i){return this.pt[i];};jsts.algorithm.distance.PointPairDistance.prototype.setMaximum=function(ptDist){if(arguments.length===2){this.setMaximum2.apply(this,arguments);return;} +this.setMaximum(ptDist.pt[0],ptDist.pt[1]);};jsts.algorithm.distance.PointPairDistance.prototype.setMaximum2=function(p0,p1){if(this.isNull){this.initialize(p0,p1);return;} +var dist=p0.distance(p1);if(dist>this.distance) +this.initialize(p0,p1,dist);};jsts.algorithm.distance.PointPairDistance.prototype.setMinimum=function(ptDist){if(arguments.length===2){this.setMinimum2.apply(this,arguments);return;} +this.setMinimum(ptDist.pt[0],ptDist.pt[1]);};jsts.algorithm.distance.PointPairDistance.prototype.setMinimum2=function(p0,p1){if(this.isNull){this.initialize(p0,p1);return;} +var dist=p0.distance(p1);if(dist1.0||densifyFrac<=0.0) +throw new jsts.error.IllegalArgumentError('Fraction is not in range (0.0 - 1.0]');this.densifyFrac=densifyFrac;};jsts.algorithm.distance.DiscreteHausdorffDistance.prototype.distance=function(){this.compute(this.g0,this.g1);return ptDist.getDistance();};jsts.algorithm.distance.DiscreteHausdorffDistance.prototype.orientedDistance=function(){this.computeOrientedDistance(this.g0,this.g1,this.ptDist);return this.ptDist.getDistance();};jsts.algorithm.distance.DiscreteHausdorffDistance.prototype.getCoordinates=function(){return ptDist.getCoordinates();};jsts.algorithm.distance.DiscreteHausdorffDistance.prototype.compute=function(g0,g1){this.computeOrientedDistance(g0,g1,this.ptDist);this.computeOrientedDistance(g1,g0,this.ptDist);};jsts.algorithm.distance.DiscreteHausdorffDistance.prototype.computeOrientedDistance=function(discreteGeom,geom,ptDist){var distFilter=new MaxPointDistanceFilter(geom);discreteGeom.apply(distFilter);ptDist.setMaximum(distFilter.getMaxPointDistance());if(this.densifyFrac>0){var fracFilter=new MaxDensifiedByFractionDistanceFilter(geom,this.densifyFrac);discreteGeom.apply(fracFilter);ptDist.setMaximum(fracFilter.getMaxPointDistance());}};})();jsts.operation.distance.GeometryLocation=function(component,segIndex,pt){this.component=component;this.segIndex=segIndex;this.pt=pt;};jsts.operation.distance.GeometryLocation.INSIDE_AREA=-1;jsts.operation.distance.GeometryLocation.prototype.component=null;jsts.operation.distance.GeometryLocation.prototype.segIndex=null;jsts.operation.distance.GeometryLocation.prototype.pt=null;jsts.operation.distance.GeometryLocation.prototype.getGeometryComponent=function(){return this.component;};jsts.operation.distance.GeometryLocation.prototype.getSegmentIndex=function(){return this.segIndex;};jsts.operation.distance.GeometryLocation.prototype.getCoordinate=function(){return this.pt;};jsts.operation.distance.GeometryLocation.prototype.isInsideArea=function(){return this.segIndex===jsts.operation.distance.GeometryLocation.INSIDE_AREA;};jsts.geom.util.PointExtracter=function(pts){this.pts=pts;};jsts.geom.util.PointExtracter.prototype=new jsts.geom.GeometryFilter();jsts.geom.util.PointExtracter.prototype.pts=null;jsts.geom.util.PointExtracter.getPoints=function(geom,list){if(list===undefined){list=[];} +if(geom instanceof jsts.geom.Point){list.push(geom);}else if(geom instanceof jsts.geom.GeometryCollection||geom instanceof jsts.geom.MultiPoint||geom instanceof jsts.geom.MultiLineString||geom instanceof jsts.geom.MultiPolygon){geom.apply(new jsts.geom.util.PointExtracter(list));} +return list;};jsts.geom.util.PointExtracter.prototype.filter=function(geom){if(geom instanceof jsts.geom.Point) +this.pts.push(geom);};jsts.noding.ScaledNoder=function(noder,scaleFactor,offsetX,offsetY){this.offsetX=offsetX?offsetX:0;this.offsetY=offsetY?offsetY:0;this.noder=noder;this.scaleFactor=scaleFactor;this.isScaled=!this.isIntegerPrecision();};jsts.noding.ScaledNoder.prototype=new jsts.noding.Noder();jsts.noding.ScaledNoder.constructor=jsts.noding.ScaledNoder;jsts.noding.ScaledNoder.prototype.noder=null;jsts.noding.ScaledNoder.prototype.scaleFactor=undefined;jsts.noding.ScaledNoder.prototype.offsetX=undefined;jsts.noding.ScaledNoder.prototype.offsetY=undefined;jsts.noding.ScaledNoder.prototype.isScaled=false;jsts.noding.ScaledNoder.prototype.isIntegerPrecision=function(){return this.scaleFactor===1.0;};jsts.noding.ScaledNoder.prototype.getNodedSubstrings=function(){var splitSS=this.noder.getNodedSubstrings();if(this.isScaled) +this.rescale(splitSS);return splitSS;};jsts.noding.ScaledNoder.prototype.computeNodes=function(inputSegStrings){var intSegStrings=inputSegStrings;if(this.isScaled) +intSegStrings=this.scale(inputSegStrings);this.noder.computeNodes(intSegStrings);};jsts.noding.ScaledNoder.prototype.scale=function(segStrings){if(segStrings instanceof Array){return this.scale2(segStrings);} +var transformed=new javascript.util.ArrayList();for(var i=segStrings.iterator();i.hasNext();){var ss=i.next();transformed.add(new jsts.noding.NodedSegmentString(this.scale(ss.getCoordinates()),ss.getData()));} +return transformed;};jsts.noding.ScaledNoder.prototype.scale2=function(pts){var roundPts=[];for(var i=0;iother.segmentIndex)return 1;if(this.coord.equals2D(other.coord))return 0;return jsts.noding.SegmentPointComparator.compare(this.segmentOctant,this.coord,other.coord);};(function(){jsts.io.GeoJSONWriter=function(){this.parser=new jsts.io.GeoJSONParser(this.geometryFactory);};jsts.io.GeoJSONWriter.prototype.write=function(geometry){var geoJson=this.parser.write(geometry);return geoJson;};})();jsts.io.OpenLayersParser=function(geometryFactory){this.geometryFactory=geometryFactory||new jsts.geom.GeometryFactory();};jsts.io.OpenLayersParser.prototype.read=function(geometry){if(geometry.CLASS_NAME==='OpenLayers.Geometry.Point'){return this.convertFromPoint(geometry);}else if(geometry.CLASS_NAME==='OpenLayers.Geometry.LineString'){return this.convertFromLineString(geometry);}else if(geometry.CLASS_NAME==='OpenLayers.Geometry.LinearRing'){return this.convertFromLinearRing(geometry);}else if(geometry.CLASS_NAME==='OpenLayers.Geometry.Polygon'){return this.convertFromPolygon(geometry);}else if(geometry.CLASS_NAME==='OpenLayers.Geometry.MultiPoint'){return this.convertFromMultiPoint(geometry);}else if(geometry.CLASS_NAME==='OpenLayers.Geometry.MultiLineString'){return this.convertFromMultiLineString(geometry);}else if(geometry.CLASS_NAME==='OpenLayers.Geometry.MultiPolygon'){return this.convertFromMultiPolygon(geometry);}else if(geometry.CLASS_NAME==='OpenLayers.Geometry.Collection'){return this.convertFromCollection(geometry);}};jsts.io.OpenLayersParser.prototype.convertFromPoint=function(point){return this.geometryFactory.createPoint(new jsts.geom.Coordinate(point.x,point.y));};jsts.io.OpenLayersParser.prototype.convertFromLineString=function(lineString){var i;var coordinates=[];for(i=0;i0.0){this.minExtent=delX;} +var delY=itemEnv.getHeight();if(delY0.0){this.minExtent=delY;}};jsts.operation.relate.RelateNodeFactory=function(){};jsts.operation.relate.RelateNodeFactory.prototype=new jsts.geomgraph.NodeFactory();jsts.operation.relate.RelateNodeFactory.prototype.createNode=function(coord){return new jsts.operation.relate.RelateNode(coord,new jsts.operation.relate.EdgeEndBundleStar());};jsts.index.quadtree.Key=function(itemEnv){this.pt=new jsts.geom.Coordinate();this.level=0;this.env=null;this.computeKey(itemEnv);};jsts.index.quadtree.Key.computeQuadLevel=function(env){var dx,dy,dMax,level;dx=env.getWidth();dy=env.getHeight();dMax=dx>dy?dx:dy;level=jsts.index.DoubleBits.exponent(dMax)+1;return level;};jsts.index.quadtree.Key.prototype.getPoint=function(){return this.pt;};jsts.index.quadtree.Key.prototype.getLevel=function(){return this.level;};jsts.index.quadtree.Key.prototype.getEnvelope=function(){return this.env;};jsts.index.quadtree.Key.prototype.getCentre=function(){var x,y;x=(this.env.getMinX()+this.env.getMaxX())/2;y=(this.env.getMinY()+this.env.getMaxY())/2;return new jsts.geom.Coordinate(x,y);};jsts.index.quadtree.Key.prototype.computeKey=function(){if(arguments[0]instanceof jsts.geom.Envelope){this.computeKeyFromEnvelope(arguments[0]);}else{this.computeKeyFromLevel(arguments[0],arguments[1]);}};jsts.index.quadtree.Key.prototype.computeKeyFromEnvelope=function(env){this.level=jsts.index.quadtree.Key.computeQuadLevel(env);this.env=new jsts.geom.Envelope();this.computeKey(this.level,env);while(!this.env.contains(env)){this.level+=1;this.computeKey(this.level,env);}};jsts.index.quadtree.Key.prototype.computeKeyFromLevel=function(level,env){var quadSize=jsts.index.DoubleBits.powerOf2(level);this.pt.x=Math.floor(env.getMinX()/quadSize)*quadSize;this.pt.y=Math.floor(env.getMinY()/quadSize)*quadSize;this.env.init(this.pt.x,this.pt.x+quadSize,this.pt.y,this.pt.y+ +quadSize);};jsts.operation.buffer.OffsetSegmentGenerator=function(precisionModel,bufParams,distance){this.seg0=new jsts.geom.LineSegment();this.seg1=new jsts.geom.LineSegment();this.offset0=new jsts.geom.LineSegment();this.offset1=new jsts.geom.LineSegment();this.precisionModel=precisionModel;this.bufParams=bufParams;this.li=new jsts.algorithm.RobustLineIntersector();this.filletAngleQuantum=Math.PI/2.0/bufParams.getQuadrantSegments();if(this.bufParams.getQuadrantSegments()>=8&&this.bufParams.getJoinStyle()===jsts.operation.buffer.BufferParameters.JOIN_ROUND){this.closingSegLengthFactor=jsts.operation.buffer.OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR;} +this.init(distance);};jsts.operation.buffer.OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR=1.0E-3;jsts.operation.buffer.OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR=1.0E-3;jsts.operation.buffer.OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR=1.0E-6;jsts.operation.buffer.OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR=80;jsts.operation.buffer.OffsetSegmentGenerator.prototype.maxCurveSegmentError=0.0;jsts.operation.buffer.OffsetSegmentGenerator.prototype.filletAngleQuantum=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.closingSegLengthFactor=1;jsts.operation.buffer.OffsetSegmentGenerator.prototype.segList=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.distance=0.0;jsts.operation.buffer.OffsetSegmentGenerator.prototype.precisionModel=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.bufParams=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.li=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.s0=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.s1=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.s2=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.seg0=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.seg1=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.offset0=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.offset1=null;jsts.operation.buffer.OffsetSegmentGenerator.prototype.side=0;jsts.operation.buffer.OffsetSegmentGenerator.prototype.hasNarrowConcaveAngle=false;jsts.operation.buffer.OffsetSegmentGenerator.prototype.hasNarrowConcaveAngle=function(){return this.hasNarrowConcaveAngle;};jsts.operation.buffer.OffsetSegmentGenerator.prototype.init=function(distance){this.distance=distance;this.maxCurveSegmentError=this.distance*(1-Math.cos(this.filletAngleQuantum/2.0));this.segList=new jsts.operation.buffer.OffsetSegmentString();this.segList.setPrecisionModel(this.precisionModel);this.segList.setMinimumVertexDistance(this.distance*jsts.operation.buffer.OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR);};jsts.operation.buffer.OffsetSegmentGenerator.prototype.initSideSegments=function(s1,s2,side){this.s1=s1;this.s2=s2;this.side=side;this.seg1.setCoordinates(this.s1,this.s2);this.computeOffsetSegment(this.seg1,this.side,this.distance,this.offset1);};jsts.operation.buffer.OffsetSegmentGenerator.prototype.getCoordinates=function(){return this.segList.getCoordinates();};jsts.operation.buffer.OffsetSegmentGenerator.prototype.closeRing=function(){this.segList.closeRing();};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addSegments=function(pt,isForward){this.segList.addPts(pt,isForward);};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addFirstSegment=function(){this.segList.addPt(this.offset1.p0);};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addLastSegment=function(){this.segList.addPt(this.offset1.p1);};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addNextSegment=function(p,addStartPoint){this.s0=this.s1;this.s1=this.s2;this.s2=p;this.seg0.setCoordinates(this.s0,this.s1);this.computeOffsetSegment(this.seg0,this.side,this.distance,this.offset0);this.seg1.setCoordinates(this.s1,this.s2);this.computeOffsetSegment(this.seg1,this.side,this.distance,this.offset1);if(this.s1.equals(this.s2)) +return;var orientation=jsts.algorithm.CGAlgorithms.computeOrientation(this.s0,this.s1,this.s2);var outsideTurn=(orientation===jsts.algorithm.CGAlgorithms.CLOCKWISE&&this.side===jsts.geomgraph.Position.LEFT)||(orientation===jsts.algorithm.CGAlgorithms.COUNTERCLOCKWISE&&this.side===jsts.geomgraph.Position.RIGHT);if(orientation==0){this.addCollinear(addStartPoint);}else if(outsideTurn){this.addOutsideTurn(orientation,addStartPoint);}else{this.addInsideTurn(orientation,addStartPoint);}};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addCollinear=function(addStartPoint){this.li.computeIntersection(this.s0,this.s1,this.s1,this.s2);var numInt=this.li.getIntersectionNum();if(numInt>=2){if(this.bufParams.getJoinStyle()===jsts.operation.buffer.BufferParameters.JOIN_BEVEL||this.bufParams.getJoinStyle()===jsts.operation.buffer.BufferParameters.JOIN_MITRE){if(addStartPoint) +this.segList.addPt(this.offset0.p1);this.segList.addPt(this.offset1.p0);}else{this.addFillet(this.s1,this.offset0.p1,this.offset1.p0,jsts.algorithm.CGAlgorithms.CLOCKWISE,this.distance);}}};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addOutsideTurn=function(orientation,addStartPoint){if(this.offset0.p1.distance(this.offset1.p0)0){var mid0=new jsts.geom.Coordinate((this.closingSegLengthFactor*this.offset0.p1.x+this.s1.x)/(this.closingSegLengthFactor+1),(this.closingSegLengthFactor*this.offset0.p1.y+this.s1.y)/(this.closingSegLengthFactor+1));this.segList.addPt(mid0);var mid1=new jsts.geom.Coordinate((this.closingSegLengthFactor*this.offset1.p0.x+this.s1.x)/(this.closingSegLengthFactor+1),(this.closingSegLengthFactor*this.offset1.p0.y+this.s1.y)/(this.closingSegLengthFactor+1));this.segList.addPt(mid1);}else{this.segList.addPt(this.s1);} +this.segList.addPt(this.offset1.p0);}}};jsts.operation.buffer.OffsetSegmentGenerator.prototype.computeOffsetSegment=function(seg,side,distance,offset){var sideSign=side===jsts.geomgraph.Position.LEFT?1:-1;var dx=seg.p1.x-seg.p0.x;var dy=seg.p1.y-seg.p0.y;var len=Math.sqrt(dx*dx+dy*dy);var ux=sideSign*distance*dx/len;var uy=sideSign*distance*dy/len;offset.p0.x=seg.p0.x-uy;offset.p0.y=seg.p0.y+ux;offset.p1.x=seg.p1.x-uy;offset.p1.y=seg.p1.y+ux;};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addLineEndCap=function(p0,p1){var seg=new jsts.geom.LineSegment(p0,p1);var offsetL=new jsts.geom.LineSegment();this.computeOffsetSegment(seg,jsts.geomgraph.Position.LEFT,this.distance,offsetL);var offsetR=new jsts.geom.LineSegment();this.computeOffsetSegment(seg,jsts.geomgraph.Position.RIGHT,this.distance,offsetR);var dx=p1.x-p0.x;var dy=p1.y-p0.y;var angle=Math.atan2(dy,dx);switch(this.bufParams.getEndCapStyle()){case jsts.operation.buffer.BufferParameters.CAP_ROUND:this.segList.addPt(offsetL.p1);this.addFillet(p1,angle+Math.PI/2,angle-Math.PI/2,jsts.algorithm.CGAlgorithms.CLOCKWISE,this.distance);this.segList.addPt(offsetR.p1);break;case jsts.operation.buffer.BufferParameters.CAP_FLAT:this.segList.addPt(offsetL.p1);this.segList.addPt(offsetR.p1);break;case jsts.operation.buffer.BufferParameters.CAP_SQUARE:var squareCapSideOffset=new jsts.geom.Coordinate();squareCapSideOffset.x=Math.abs(this.distance)*Math.cos(angle);squareCapSideOffset.y=Math.abs(this.distance)*Math.sin(angle);var squareCapLOffset=new jsts.geom.Coordinate(offsetL.p1.x+ +squareCapSideOffset.x,offsetL.p1.y+squareCapSideOffset.y);var squareCapROffset=new jsts.geom.Coordinate(offsetR.p1.x+ +squareCapSideOffset.x,offsetR.p1.y+squareCapSideOffset.y);this.segList.addPt(squareCapLOffset);this.segList.addPt(squareCapROffset);break;}};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addMitreJoin=function(p,offset0,offset1,distance){var isMitreWithinLimit=true;var intPt=null;try{intPt=jsts.algorithm.HCoordinate.intersection(offset0.p0,offset0.p1,offset1.p0,offset1.p1);var mitreRatio=distance<=0.0?1.0:intPt.distance(p)/Math.abs(distance);if(mitreRatio>this.bufParams.getMitreLimit()) +this.isMitreWithinLimit=false;}catch(e){if(e instanceof jsts.error.NotRepresentableError){intPt=new jsts.geom.Coordinate(0,0);this.isMitreWithinLimit=false;}} +if(isMitreWithinLimit){this.segList.addPt(intPt);}else{this.addLimitedMitreJoin(offset0,offset1,distance,bufParams.getMitreLimit());}};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addLimitedMitreJoin=function(offset0,offset1,distance,mitreLimit){var basePt=this.seg0.p1;var ang0=jsts.algorithm.Angle.angle(basePt,this.seg0.p0);var ang1=jsts.algorithm.Angle.angle(basePt,this.seg1.p1);var angDiff=jsts.algorithm.Angle.angleBetweenOriented(this.seg0.p0,basePt,this.seg1.p1);var angDiffHalf=angDiff/2;var midAng=jsts.algorithm.Angle.normalize(ang0+angDiffHalf);var mitreMidAng=jsts.algorithm.Angle.normalize(midAng+Math.PI);var mitreDist=mitreLimit*distance;var bevelDelta=mitreDist*Math.abs(Math.sin(angDiffHalf));var bevelHalfLen=distance-bevelDelta;var bevelMidX=basePt.x+mitreDist*Math.cos(mitreMidAng);var bevelMidY=basePt.y+mitreDist*Math.sin(mitreMidAng);var bevelMidPt=new jsts.geom.Coordinate(bevelMidX,bevelMidY);var mitreMidLine=new jsts.geom.LineSegment(basePt,bevelMidPt);var bevelEndLeft=mitreMidLine.pointAlongOffset(1.0,bevelHalfLen);var bevelEndRight=mitreMidLine.pointAlongOffset(1.0,-bevelHalfLen);if(this.side==jsts.geomgraph.Position.LEFT){this.segList.addPt(bevelEndLeft);this.segList.addPt(bevelEndRight);}else{this.segList.addPt(bevelEndRight);this.segList.addPt(bevelEndLeft);}};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addBevelJoin=function(offset0,offset1){this.segList.addPt(offset0.p1);this.segList.addPt(offset1.p0);};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addFillet=function(p,p0,p1,direction,radius){if(!(p1 instanceof jsts.geom.Coordinate)){this.addFillet2.apply(this,arguments);return;} +var dx0=p0.x-p.x;var dy0=p0.y-p.y;var startAngle=Math.atan2(dy0,dx0);var dx1=p1.x-p.x;var dy1=p1.y-p.y;var endAngle=Math.atan2(dy1,dx1);if(direction===jsts.algorithm.CGAlgorithms.CLOCKWISE){if(startAngle<=endAngle) +startAngle+=2.0*Math.PI;}else{if(startAngle>=endAngle) +startAngle-=2.0*Math.PI;} +this.segList.addPt(p0);this.addFillet(p,startAngle,endAngle,direction,radius);this.segList.addPt(p1);};jsts.operation.buffer.OffsetSegmentGenerator.prototype.addFillet2=function(p,startAngle,endAngle,direction,radius){var directionFactor=direction===jsts.algorithm.CGAlgorithms.CLOCKWISE?-1:1;var totalAngle=Math.abs(startAngle-endAngle);var nSegs=parseInt((totalAngle/this.filletAngleQuantum+0.5));if(nSegs<1) +return;var initAngle,currAngleInc;initAngle=0.0;currAngleInc=totalAngle/nSegs;var currAngle=initAngle;var pt=new jsts.geom.Coordinate();while(currAngle0){minCoord=coordinates[i];}} +return minCoord;};jsts.geom.CoordinateArrays.scroll=function(coordinates,firstCoordinate){var i=jsts.geom.CoordinateArrays.indexOf(firstCoordinate,coordinates);if(i<0) +return;var newCoordinates=coordinates.slice(i).concat(coordinates.slice(0,i));for(i=0;imaxx){minx=p2.x;maxx=p1.x;} +if(this.p.x>=minx&&this.p.x<=maxx){this.isPointOnSegment=true;} +return;} +if(((p1.y>this.p.y)&&(p2.y<=this.p.y))||((p2.y>this.p.y)&&(p1.y<=this.p.y))){var x1=p1.x-this.p.x;var y1=p1.y-this.p.y;var x2=p2.x-this.p.x;var y2=p2.y-this.p.y;var xIntSign=jsts.algorithm.RobustDeterminant.signOfDet2x2(x1,y1,x2,y2);if(xIntSign===0.0){this.isPointOnSegment=true;return;} +if(y20.0){this.crossingCount++;}}};jsts.algorithm.RayCrossingCounter.prototype.isOnSegment=function(){return jsts.geom.isPointOnSegment;};jsts.algorithm.RayCrossingCounter.prototype.getLocation=function(){if(this.isPointOnSegment) +return jsts.geom.Location.BOUNDARY;if((this.crossingCount%2)===1){return jsts.geom.Location.INTERIOR;} +return jsts.geom.Location.EXTERIOR;};jsts.algorithm.RayCrossingCounter.prototype.isPointInPolygon=function(){return this.getLocation()!==jsts.geom.Location.EXTERIOR;};jsts.operation.BoundaryOp=function(geom,bnRule){this.geom=geom;this.geomFact=geom.getFactory();this.bnRule=bnRule||jsts.algorithm.BoundaryNodeRule.MOD2_BOUNDARY_RULE;};jsts.operation.BoundaryOp.prototype.geom=null;jsts.operation.BoundaryOp.prototype.geomFact=null;jsts.operation.BoundaryOp.prototype.bnRule=null;jsts.operation.BoundaryOp.prototype.getBoundary=function(){if(this.geom instanceof jsts.geom.LineString)return this.boundaryLineString(this.geom);if(this.geom instanceof jsts.geom.MultiLineString)return this.boundaryMultiLineString(this.geom);return this.geom.getBoundary();};jsts.operation.BoundaryOp.prototype.getEmptyMultiPoint=function(){return this.geomFact.createMultiPoint(null);};jsts.operation.BoundaryOp.prototype.boundaryMultiLineString=function(mLine){if(this.geom.isEmpty()){return this.getEmptyMultiPoint();} +var bdyPts=this.computeBoundaryCoordinates(mLine);if(bdyPts.length==1){return this.geomFact.createPoint(bdyPts[0]);} +return this.geomFact.createMultiPoint(bdyPts);};jsts.operation.BoundaryOp.prototype.endpoints=null;jsts.operation.BoundaryOp.prototype.computeBoundaryCoordinates=function(mLine){var i,line,endpoint,bdyPts=[];this.endpoints=[];for(i=0;i0.0&&this.isErodedCompletely(hole,-this.distance)) +continue;this.addPolygonRing(holeCoord,offsetDistance,jsts.geomgraph.Position.opposite(offsetSide),jsts.geom.Location.INTERIOR,jsts.geom.Location.EXTERIOR);}};jsts.operation.buffer.OffsetCurveSetBuilder.prototype.addPolygonRing=function(coord,offsetDistance,side,cwLeftLoc,cwRightLoc){if(offsetDistance==0.0&&coord.length=jsts.geom.LinearRing.MINIMUM_VALID_SIZE&&jsts.algorithm.CGAlgorithms.isCCW(coord)){leftLoc=cwRightLoc;rightLoc=cwLeftLoc;side=jsts.geomgraph.Position.opposite(side);} +var curve=this.curveBuilder.getRingCurve(coord,side,offsetDistance);this.addCurve(curve,leftLoc,rightLoc);};jsts.operation.buffer.OffsetCurveSetBuilder.prototype.isErodedCompletely=function(ring,bufferDistance){var ringCoord=ring.getCoordinates();var minDiam=0.0;if(ringCoord.length<4) +return bufferDistance<0;if(ringCoord.length==4) +return this.isTriangleErodedCompletely(ringCoord,bufferDistance);var env=ring.getEnvelopeInternal();var envMinDimension=Math.min(env.getHeight(),env.getWidth());if(bufferDistance<0.0&&2*Math.abs(bufferDistance)>envMinDimension) +return true;return false;};jsts.operation.buffer.OffsetCurveSetBuilder.prototype.isTriangleErodedCompletely=function(triangleCoord,bufferDistance){var tri=new jsts.geom.Triangle(triangleCoord[0],triangleCoord[1],triangleCoord[2]);var inCentre=tri.inCentre();var distToCentre=jsts.algorithm.CGAlgorithms.distancePointLine(inCentre,tri.p0,tri.p1);return distToCentre=1&&de.getDepth(jsts.geomgraph.Position.LEFT)<=0&&!de.isInteriorAreaEdge()){de.setInResult(true);}}};jsts.operation.buffer.BufferSubgraph.prototype.compareTo=function(o){var graph=o;if(this.rightMostCoord.xgraph.rightMostCoord.x){return 1;} +return 0;};jsts.geom.util.GeometryExtracter=function(clz,comps){this.clz=clz;this.comps=comps;};jsts.geom.util.GeometryExtracter.prototype=new jsts.geom.GeometryFilter();jsts.geom.util.GeometryExtracter.prototype.clz=null;jsts.geom.util.GeometryExtracter.prototype.comps=null;jsts.geom.util.GeometryExtracter.extract=function(geom,clz,list){list=list||new javascript.util.ArrayList();if(geom instanceof clz){list.add(geom);} +else if(geom instanceof jsts.geom.GeometryCollection||geom instanceof jsts.geom.MultiPoint||geom instanceof jsts.geom.MultiLineString||geom instanceof jsts.geom.MultiPolygon){geom.apply(new jsts.geom.util.GeometryExtracter(clz,list));} +return list;};jsts.geom.util.GeometryExtracter.prototype.filter=function(geom){if(this.clz===null||geom instanceof this.clz){this.comps.add(geom);}};(function(){var OverlayOp=jsts.operation.overlay.OverlayOp;var SnapOverlayOp=jsts.operation.overlay.snap.SnapOverlayOp;var SnapIfNeededOverlayOp=function(g1,g2){this.geom=[];this.geom[0]=g1;this.geom[1]=g2;};SnapIfNeededOverlayOp.overlayOp=function(g0,g1,opCode){var op=new SnapIfNeededOverlayOp(g0,g1);return op.getResultGeometry(opCode);};SnapIfNeededOverlayOp.intersection=function(g0,g1){return overlayOp(g0,g1,OverlayOp.INTERSECTION);};SnapIfNeededOverlayOp.union=function(g0,g1){return overlayOp(g0,g1,OverlayOp.UNION);};SnapIfNeededOverlayOp.difference=function(g0,g1){return overlayOp(g0,g1,OverlayOp.DIFFERENCE);};SnapIfNeededOverlayOp.symDifference=function(g0,g1){return overlayOp(g0,g1,OverlayOp.SYMDIFFERENCE);};SnapIfNeededOverlayOp.prototype.geom=null;SnapIfNeededOverlayOp.prototype.getResultGeometry=function(opCode){var result=null;var isSuccess=false;var savedException=null;try{result=OverlayOp.overlayOp(this.geom[0],this.geom[1],opCode);var isValid=true;if(isValid) +isSuccess=true;}catch(ex){savedException=ex;} +if(!isSuccess){try{result=SnapOverlayOp.overlayOp(this.geom[0],this.geom[1],opCode);}catch(ex){throw savedException;}} +return result;};jsts.operation.overlay.snap.SnapIfNeededOverlayOp=SnapIfNeededOverlayOp;})();(function(){var GeometryExtracter=jsts.geom.util.GeometryExtracter;var CascadedPolygonUnion=jsts.operation.union.CascadedPolygonUnion;var PointGeometryUnion=jsts.operation.union.PointGeometryUnion;var OverlayOp=jsts.operation.overlay.OverlayOp;var SnapIfNeededOverlayOp=jsts.operation.overlay.snap.SnapIfNeededOverlayOp;var ArrayList=javascript.util.ArrayList;jsts.operation.union.UnaryUnionOp=function(geoms,geomFact){this.polygons=new ArrayList();this.lines=new ArrayList();this.points=new ArrayList();if(geomFact){this.geomFact=geomFact;} +this.extract(geoms);};jsts.operation.union.UnaryUnionOp.union=function(geoms,geomFact){var op=new jsts.operation.union.UnaryUnionOp(geoms,geomFact);return op.union();};jsts.operation.union.UnaryUnionOp.prototype.polygons=null;jsts.operation.union.UnaryUnionOp.prototype.lines=null;jsts.operation.union.UnaryUnionOp.prototype.points=null;jsts.operation.union.UnaryUnionOp.prototype.geomFact=null;jsts.operation.union.UnaryUnionOp.prototype.extract=function(geoms){if(geoms instanceof ArrayList){for(var i=geoms.iterator();i.hasNext();){var geom=i.next();this.extract(geom);}}else{if(this.geomFact===null){this.geomFact=geoms.getFactory();} +GeometryExtracter.extract(geoms,jsts.geom.Polygon,this.polygons);GeometryExtracter.extract(geoms,jsts.geom.LineString,this.lines);GeometryExtracter.extract(geoms,jsts.geom.Point,this.points);}};jsts.operation.union.UnaryUnionOp.prototype.union=function(){if(this.geomFact===null){return null;} +var unionPoints=null;if(this.points.size()>0){var ptGeom=this.geomFact.buildGeometry(this.points);unionPoints=this.unionNoOpt(ptGeom);} +var unionLines=null;if(this.lines.size()>0){var lineGeom=this.geomFact.buildGeometry(this.lines);unionLines=this.unionNoOpt(lineGeom);} +var unionPolygons=null;if(this.polygons.size()>0){unionPolygons=CascadedPolygonUnion.union(this.polygons);} +var unionLA=this.unionWithNull(unionLines,unionPolygons);var union=null;if(unionPoints===null){union=unionLA;}else if(unionLA===null){union=unionPoints;}else{union=PointGeometryUnion(unionPoints,unionLA);} +if(union===null){return this.geomFact.createGeometryCollection(null);} +return union;};jsts.operation.union.UnaryUnionOp.prototype.unionWithNull=function(g0,g1){if(g0===null&&g1===null){return null;} +if(g1===null){return g0;} +if(g0===null){return g1;} +return g0.union(g1);};jsts.operation.union.UnaryUnionOp.prototype.unionNoOpt=function(g0){var empty=this.geomFact.createPoint(null);return SnapIfNeededOverlayOp.overlayOp(g0,empty,OverlayOp.UNION);};}());jsts.index.kdtree.KdNode=function(){this.left=null;this.right=null;this.count=1;if(arguments.length===2){this.initializeFromCoordinate.apply(this,arguments[0],arguments[1]);}else if(arguments.length===3){this.initializeFromXY.apply(this,arguments[0],arguments[1],arguments[2]);}};jsts.index.kdtree.KdNode.prototype.initializeFromXY=function(x,y,data){this.p=new jsts.geom.Coordinate(x,y);this.data=data;};jsts.index.kdtree.KdNode.prototype.initializeFromCoordinate=function(p,data){this.p=p;this.data=data;};jsts.index.kdtree.KdNode.prototype.getX=function(){return this.p.x;};jsts.index.kdtree.KdNode.prototype.getY=function(){return this.p.y;};jsts.index.kdtree.KdNode.prototype.getCoordinate=function(){return this.p;};jsts.index.kdtree.KdNode.prototype.getData=function(){return this.data;};jsts.index.kdtree.KdNode.prototype.getLeft=function(){return this.left;};jsts.index.kdtree.KdNode.prototype.getRight=function(){return this.right;};jsts.index.kdtree.KdNode.prototype.increment=function(){this.count+=1;};jsts.index.kdtree.KdNode.prototype.getCount=function(){return this.count;};jsts.index.kdtree.KdNode.prototype.isRepeated=function(){return count>1;};jsts.index.kdtree.KdNode.prototype.setLeft=function(left){this.left=left;};jsts.index.kdtree.KdNode.prototype.setRight=function(right){this.right=right;};(function(){jsts.geom.MultiLineString=function(geometries,factory){this.geometries=geometries||[];this.factory=factory;};jsts.geom.MultiLineString.prototype=new jsts.geom.GeometryCollection();jsts.geom.MultiLineString.constructor=jsts.geom.MultiLineString;jsts.geom.MultiLineString.prototype.getBoundary=function(){return(new jsts.operation.BoundaryOp(this)).getBoundary();};jsts.geom.MultiLineString.prototype.equalsExact=function(other,tolerance){if(!this.isEquivalentClass(other)){return false;} +return jsts.geom.GeometryCollection.prototype.equalsExact.call(this,other,tolerance);};jsts.geom.MultiLineString.prototype.CLASS_NAME='jsts.geom.MultiLineString';})();jsts.algorithm.BoundaryNodeRule=function(){};jsts.algorithm.BoundaryNodeRule.prototype.isInBoundary=function(boundaryCount){throw new jsts.error.AbstractMethodInvocationError();};jsts.algorithm.Mod2BoundaryNodeRule=function(){};jsts.algorithm.Mod2BoundaryNodeRule.prototype=new jsts.algorithm.BoundaryNodeRule();jsts.algorithm.Mod2BoundaryNodeRule.prototype.isInBoundary=function(boundaryCount){return boundaryCount%2===1;};jsts.algorithm.BoundaryNodeRule.MOD2_BOUNDARY_RULE=new jsts.algorithm.Mod2BoundaryNodeRule();jsts.algorithm.BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE=jsts.algorithm.BoundaryNodeRule.MOD2_BOUNDARY_RULE;jsts.operation.buffer.BufferBuilder=function(bufParams){this.bufParams=bufParams;this.edgeList=new jsts.geomgraph.EdgeList();};jsts.operation.buffer.BufferBuilder.depthDelta=function(label){var lLoc=label.getLocation(0,jsts.geomgraph.Position.LEFT);var rLoc=label.getLocation(0,jsts.geomgraph.Position.RIGHT);if(lLoc===jsts.geom.Location.INTERIOR&&rLoc===jsts.geom.Location.EXTERIOR) +return 1;else if(lLoc===jsts.geom.Location.EXTERIOR&&rLoc===jsts.geom.Location.INTERIOR) +return-1;return 0;};jsts.operation.buffer.BufferBuilder.prototype.bufParams=null;jsts.operation.buffer.BufferBuilder.prototype.workingPrecisionModel=null;jsts.operation.buffer.BufferBuilder.prototype.workingNoder=null;jsts.operation.buffer.BufferBuilder.prototype.geomFact=null;jsts.operation.buffer.BufferBuilder.prototype.graph=null;jsts.operation.buffer.BufferBuilder.prototype.edgeList=null;jsts.operation.buffer.BufferBuilder.prototype.setWorkingPrecisionModel=function(pm){this.workingPrecisionModel=pm;};jsts.operation.buffer.BufferBuilder.prototype.setNoder=function(noder){this.workingNoder=noder;};jsts.operation.buffer.BufferBuilder.prototype.buffer=function(g,distance){var precisionModel=this.workingPrecisionModel;if(precisionModel===null) +precisionModel=g.getPrecisionModel();this.geomFact=g.getFactory();var curveBuilder=new jsts.operation.buffer.OffsetCurveBuilder(precisionModel,this.bufParams);var curveSetBuilder=new jsts.operation.buffer.OffsetCurveSetBuilder(g,distance,curveBuilder);var bufferSegStrList=curveSetBuilder.getCurves();if(bufferSegStrList.size()<=0){return this.createEmptyResultGeometry();} +this.computeNodedEdges(bufferSegStrList,precisionModel);this.graph=new jsts.geomgraph.PlanarGraph(new jsts.operation.overlay.OverlayNodeFactory());this.graph.addEdges(this.edgeList.getEdges());var subgraphList=this.createSubgraphs(this.graph);var polyBuilder=new jsts.operation.overlay.PolygonBuilder(this.geomFact);this.buildSubgraphs(subgraphList,polyBuilder);var resultPolyList=polyBuilder.getPolygons();if(resultPolyList.size()<=0){return this.createEmptyResultGeometry();} +var resultGeom=this.geomFact.buildGeometry(resultPolyList);return resultGeom;};jsts.operation.buffer.BufferBuilder.prototype.getNoder=function(precisionModel){if(this.workingNoder!==null) +return this.workingNoder;var noder=new jsts.noding.MCIndexNoder();var li=new jsts.algorithm.RobustLineIntersector();li.setPrecisionModel(precisionModel);noder.setSegmentIntersector(new jsts.noding.IntersectionAdder(li));return noder;};jsts.operation.buffer.BufferBuilder.prototype.computeNodedEdges=function(bufferSegStrList,precisionModel){var noder=this.getNoder(precisionModel);noder.computeNodes(bufferSegStrList);var nodedSegStrings=noder.getNodedSubstrings();for(var i=nodedSegStrings.iterator();i.hasNext();){var segStr=i.next();var oldLabel=segStr.getData();var edge=new jsts.geomgraph.Edge(segStr.getCoordinates(),new jsts.geomgraph.Label(oldLabel));this.insertUniqueEdge(edge);}};jsts.operation.buffer.BufferBuilder.prototype.insertUniqueEdge=function(e){var existingEdge=this.edgeList.findEqualEdge(e);if(existingEdge!=null){var existingLabel=existingEdge.getLabel();var labelToMerge=e.getLabel();if(!existingEdge.isPointwiseEqual(e)){labelToMerge=new jsts.geomgraph.Label(e.getLabel());labelToMerge.flip();} +existingLabel.merge(labelToMerge);var mergeDelta=jsts.operation.buffer.BufferBuilder.depthDelta(labelToMerge);var existingDelta=existingEdge.getDepthDelta();var newDelta=existingDelta+mergeDelta;existingEdge.setDepthDelta(newDelta);}else{this.edgeList.add(e);e.setDepthDelta(jsts.operation.buffer.BufferBuilder.depthDelta(e.getLabel()));}};jsts.operation.buffer.BufferBuilder.prototype.createSubgraphs=function(graph){var subgraphList=[];for(var i=graph.getNodes().iterator();i.hasNext();){var node=i.next();if(!node.isVisited()){var subgraph=new jsts.operation.buffer.BufferSubgraph();subgraph.create(node);subgraphList.push(subgraph);}} +var compare=function(a,b){return a.compareTo(b);};subgraphList.sort(compare);subgraphList.reverse();return subgraphList;};jsts.operation.buffer.BufferBuilder.prototype.buildSubgraphs=function(subgraphList,polyBuilder){var processedGraphs=[];for(var i=0;i0){loc=jsts.geomgraph.GeometryGraph.determineBoundary(boundaryNodeRule,boundaryCount);} +this.label.setLocation(geomIndex,loc);};jsts.operation.relate.EdgeEndBundle.prototype.computeLabelSides=function(geomIndex){this.computeLabelSide(geomIndex,jsts.geomgraph.Position.LEFT);this.computeLabelSide(geomIndex,jsts.geomgraph.Position.RIGHT);};jsts.operation.relate.EdgeEndBundle.prototype.computeLabelSide=function(geomIndex,side){for(var i=0;i=0||actualDimensionValue===Dimension.TRUE)){return true;} +if(requiredDimensionSymbol==='F'&&actualDimensionValue===Dimension.FALSE){return true;} +if(requiredDimensionSymbol==='0'&&actualDimensionValue===Dimension.P){return true;} +if(requiredDimensionSymbol==='1'&&actualDimensionValue===Dimension.L){return true;} +if(requiredDimensionSymbol==='2'&&actualDimensionValue===Dimension.A){return true;} +return false;};jsts.geom.IntersectionMatrix.matches2=function(actualDimensionSymbols,requiredDimensionSymbols){var m=new jsts.geom.IntersectionMatrix(actualDimensionSymbols);return m.matches(requiredDimensionSymbols);};jsts.geom.IntersectionMatrix.prototype.set=function(row,column,dimensionValue){if(typeof row==='string'){this.set2(row);return;} +this.matrix[row][column]=dimensionValue;};jsts.geom.IntersectionMatrix.prototype.set2=function(dimensionSymbols){for(var i=0;i=0&&column>=0){this.setAtLeast(row,column,minimumDimensionValue);}};jsts.geom.IntersectionMatrix.prototype.setAtLeast2=function(minimumDimensionSymbols){var i;for(i=0;idimensionOfGeometryB){return this.isTouches(dimensionOfGeometryB,dimensionOfGeometryA);} +if((dimensionOfGeometryA==Dimension.A&&dimensionOfGeometryB==Dimension.A)||(dimensionOfGeometryA==Dimension.L&&dimensionOfGeometryB==Dimension.L)||(dimensionOfGeometryA==Dimension.L&&dimensionOfGeometryB==Dimension.A)||(dimensionOfGeometryA==Dimension.P&&dimensionOfGeometryB==Dimension.A)||(dimensionOfGeometryA==Dimension.P&&dimensionOfGeometryB==Dimension.L)){return this.matrix[Location.INTERIOR][Location.INTERIOR]===Dimension.FALSE&&(jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.BOUNDARY],'T')||jsts.geom.IntersectionMatrix.matches(this.matrix[Location.BOUNDARY][Location.INTERIOR],'T')||jsts.geom.IntersectionMatrix.matches(this.matrix[Location.BOUNDARY][Location.BOUNDARY],'T'));} +return false;};jsts.geom.IntersectionMatrix.prototype.isCrosses=function(dimensionOfGeometryA,dimensionOfGeometryB){if((dimensionOfGeometryA==Dimension.P&&dimensionOfGeometryB==Dimension.L)||(dimensionOfGeometryA==Dimension.P&&dimensionOfGeometryB==Dimension.A)||(dimensionOfGeometryA==Dimension.L&&dimensionOfGeometryB==Dimension.A)){return jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.INTERIOR],'T')&&jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.EXTERIOR],'T');} +if((dimensionOfGeometryA==Dimension.L&&dimensionOfGeometryB==Dimension.P)||(dimensionOfGeometryA==Dimension.A&&dimensionOfGeometryB==Dimension.P)||(dimensionOfGeometryA==Dimension.A&&dimensionOfGeometryB==Dimension.L)){return jsts.geom.IntersectionMatrix.matches(matrix[Location.INTERIOR][Location.INTERIOR],'T')&&jsts.geom.IntersectionMatrix.matches(this.matrix[Location.EXTERIOR][Location.INTERIOR],'T');} +if(dimensionOfGeometryA===Dimension.L&&dimensionOfGeometryB===Dimension.L){return this.matrix[Location.INTERIOR][Location.INTERIOR]===0;} +return false;};jsts.geom.IntersectionMatrix.prototype.isWithin=function(){return jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.INTERIOR],'T')&&this.matrix[Location.INTERIOR][Location.EXTERIOR]==Dimension.FALSE&&this.matrix[Location.BOUNDARY][Location.EXTERIOR]==Dimension.FALSE;};jsts.geom.IntersectionMatrix.prototype.isContains=function(){return jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.INTERIOR],'T')&&this.matrix[Location.EXTERIOR][Location.INTERIOR]==Dimension.FALSE&&this.matrix[Location.EXTERIOR][Location.BOUNDARY]==Dimension.FALSE;};jsts.geom.IntersectionMatrix.prototype.isCovers=function(){var hasPointInCommon=jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.INTERIOR],'T')||jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.BOUNDARY],'T')||jsts.geom.IntersectionMatrix.matches(this.matrix[Location.BOUNDARY][Location.INTERIOR],'T')||jsts.geom.IntersectionMatrix.matches(this.matrix[Location.BOUNDARY][Location.BOUNDARY],'T');return hasPointInCommon&&this.matrix[Location.EXTERIOR][Location.INTERIOR]==Dimension.FALSE&&this.matrix[Location.EXTERIOR][Location.BOUNDARY]==Dimension.FALSE;};jsts.geom.IntersectionMatrix.prototype.isCoveredBy=function(){var hasPointInCommon=jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.INTERIOR],'T')||jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.BOUNDARY],'T')||jsts.geom.IntersectionMatrix.matches(this.matrix[Location.BOUNDARY][Location.INTERIOR],'T')||jsts.geom.IntersectionMatrix.matches(this.matrix[Location.BOUNDARY][Location.BOUNDARY],'T');return hasPointInCommon&&this.matrix[Location.INTERIOR][Location.EXTERIOR]===Dimension.FALSE&&this.matrix[Location.BOUNDARY][Location.EXTERIOR]===Dimension.FALSE;};jsts.geom.IntersectionMatrix.prototype.isEquals=function(dimensionOfGeometryA,dimensionOfGeometryB){if(dimensionOfGeometryA!==dimensionOfGeometryB){return false;} +return jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.INTERIOR],'T')&&this.matrix[Location.EXTERIOR][Location.INTERIOR]===Dimension.FALSE&&this.matrix[Location.INTERIOR][Location.EXTERIOR]===Dimension.FALSE&&this.matrix[Location.EXTERIOR][Location.BOUNDARY]===Dimension.FALSE&&this.matrix[Location.BOUNDARY][Location.EXTERIOR]===Dimension.FALSE;};jsts.geom.IntersectionMatrix.prototype.isOverlaps=function(dimensionOfGeometryA,dimensionOfGeometryB){if((dimensionOfGeometryA==Dimension.P&&dimensionOfGeometryB===Dimension.P)||(dimensionOfGeometryA==Dimension.A&&dimensionOfGeometryB===Dimension.A)){return jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.INTERIOR],'T')&&jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.EXTERIOR],'T')&&jsts.geom.IntersectionMatrix.matches(this.matrix[Location.EXTERIOR][Location.INTERIOR],'T');} +if(dimensionOfGeometryA===Dimension.L&&dimensionOfGeometryB===Dimension.L){return this.matrix[Location.INTERIOR][Location.INTERIOR]==1&&jsts.geom.IntersectionMatrix.matches(this.matrix[Location.INTERIOR][Location.EXTERIOR],'T')&&jsts.geom.IntersectionMatrix.matches(this.matrix[Location.EXTERIOR][Location.INTERIOR],'T');} +return false;};jsts.geom.IntersectionMatrix.prototype.matches=function(requiredDimensionSymbols){if(requiredDimensionSymbols.length!=9){throw new jsts.error.IllegalArgumentException('Should be length 9: '+ +requiredDimensionSymbols);} +for(var ai=0;ai<3;ai++){for(var bi=0;bi<3;bi++){if(!jsts.geom.IntersectionMatrix.matches(this.matrix[ai][bi],requiredDimensionSymbols.charAt(3*ai+bi))){return false;}}} +return true;};jsts.geom.IntersectionMatrix.prototype.transpose=function(){var temp=matrix[1][0];this.matrix[1][0]=this.matrix[0][1];this.matrix[0][1]=temp;temp=this.matrix[2][0];this.matrix[2][0]=this.matrix[0][2];this.matrix[0][2]=temp;temp=this.matrix[2][1];this.matrix[2][1]=this.matrix[1][2];this.matrix[1][2]=temp;return this;};jsts.geom.IntersectionMatrix.prototype.toString=function(){var ai,bi,buf='';for(ai=0;ai<3;ai++){for(bi=0;bi<3;bi++){buf+=Dimension.toDimensionSymbol(this.matrix[ai][bi]);}} +return buf;};})();jsts.triangulate.quadedge.LastFoundQuadEdgeLocator=function(subdiv){this.subdiv=subdiv;this.lastEdge=null;this.init();};jsts.triangulate.quadedge.LastFoundQuadEdgeLocator.prototype.init=function(){this.lastEdge=this.findEdge();};jsts.triangulate.quadedge.LastFoundQuadEdgeLocator.prototype.findEdge=function(){var edges=this.subdiv.getEdges();return edges[0];};jsts.triangulate.quadedge.LastFoundQuadEdgeLocator.prototype.locate=function(v){if(!this.lastEdge.isLive()){this.init();} +var e=this.subdiv.locateFromEdge(v,this.lastEdge);this.lastEdge=e;return e;};jsts.io.WKTWriter=function(){this.parser=new jsts.io.WKTParser(this.geometryFactory);};jsts.io.WKTWriter.prototype.write=function(geometry){var wkt=this.parser.write(geometry);return wkt;};jsts.io.WKTWriter.toLineString=function(p0,p1){if(arguments.length!==2){throw new jsts.error.NotImplementedError();} +return'LINESTRING ( '+p0.x+' '+p0.y+', '+p1.x+' '+p1.y+' )';};jsts.io.WKTReader=function(geometryFactory){this.geometryFactory=geometryFactory||new jsts.geom.GeometryFactory();this.precisionModel=this.geometryFactory.getPrecisionModel();this.parser=new jsts.io.WKTParser(this.geometryFactory);};jsts.io.WKTReader.prototype.read=function(wkt){var geometry=this.parser.read(wkt);if(this.precisionModel.getType()===jsts.geom.PrecisionModel.FIXED){this.reducePrecision(geometry);} +return geometry;};jsts.io.WKTReader.prototype.reducePrecision=function(geometry){var i,len;if(geometry.coordinate){this.precisionModel.makePrecise(geometry.coordinate);}else if(geometry.points){for(i=0,len=geometry.points.length;ideltaY){offset=deltaX*10.0;}else{offset=deltaY*10.0;} +this.frameVertex[0]=new jsts.triangulate.quadedge.Vertex((env.getMaxX()+env.getMinX())/2.0,env.getMaxY() ++offset);this.frameVertex[1]=new jsts.triangulate.quadedge.Vertex(env.getMinX()-offset,env.getMinY()-offset);this.frameVertex[2]=new jsts.triangulate.quadedge.Vertex(env.getMaxX()+offset,env.getMinY()-offset);this.frameEnv=new jsts.geom.Envelope(this.frameVertex[0].getCoordinate(),this.frameVertex[1].getCoordinate());this.frameEnv.expandToInclude(this.frameVertex[2].getCoordinate());};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.initSubdiv=function(){var ea,eb,ec;ea=this.makeEdge(this.frameVertex[0],this.frameVertex[1]);eb=this.makeEdge(this.frameVertex[1],this.frameVertex[2]);jsts.triangulate.quadedge.QuadEdge.splice(ea.sym(),eb);ec=this.makeEdge(this.frameVertex[2],this.frameVertex[0]);jsts.triangulate.quadedge.QuadEdge.splice(eb.sym(),ec);jsts.triangulate.quadedge.QuadEdge.splice(ec.sym(),ea);return ea;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.getTolerance=function(){return this.tolerance;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.getEnvelope=function(){return new jsts.geom.Envelope(this.frameEnv);};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.getEdges=function(){if(arguments.length>0){return this.getEdgesByFactory(arguments[0]);}else{return this.quadEdges;}};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.setLocator=function(locator){this.locator=locator;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.makeEdge=function(o,d){var q=jsts.triangulate.quadedge.QuadEdge.makeEdge(o,d);this.quadEdges.push(q);return q;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.connect=function(a,b){var q=jsts.triangulate.quadedge.QuadEdge.connect(a,b);this.quadEdges.push(q);return q;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.delete_jsts=function(e){jsts.triangulate.quadedge.QuadEdge.splice(e,e.oPrev());jsts.triangulate.quadedge.QuadEdge.splice(e.sym(),e.sym().oPrev());var eSym,eRot,eRotSym;e.eSym=e.sym();eRot=e.rot;eRotSym=e.rot.sym();var idx=this.quadEdges.indexOf(e);if(idx!==-1){this.quadEdges.splice(idx,1);} +idx=this.quadEdges.indexOf(eSym);if(idx!==-1){this.quadEdges.splice(idx,1);} +idx=this.quadEdges.indexOf(eRot);if(idx!==-1){this.quadEdges.splice(idx,1);} +idx=this.quadEdges.indexOf(eRotSym);if(idx!==-1){this.quadEdges.splice(idx,1);} +e.delete_jsts();eSym.delete_jsts();eRot.delete_jsts();eRotSym.delete_jsts();};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.locateFromEdge=function(v,startEdge){var iter=0,maxIter=this.quadEdges.length,e;e=startEdge;while(true){iter++;if(iter>maxIter){throw new jsts.error.LocateFailureError(e.toLineSegment());} +if((v.equals(e.orig()))||(v.equals(e.dest()))){break;}else if(v.rightOf(e)){e=e.sym();}else if(!v.rightOf(e.oNext())){e=e.oNext();}else if(!v.rightOf(e.dPrev())){e=e.dPrev();}else{break;}} +return e;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.locate=function(){if(arguments.length===1){if(arguments[0]instanceof jsts.triangulate.quadedge.Vertex){return this.locateByVertex(arguments[0]);}else{return this.locateByCoordinate(arguments[0]);}}else{return this.locateByCoordinates(arguments[0],arguments[1]);}};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.locateByVertex=function(v){return this.locator.locate(v);};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.locateByCoordinate=function(p){return this.locator.locate(new jsts.triangulate.quadedge.Vertex(p));};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.locateByCoordinates=function(p0,p1){var e,base,locEdge;var e=this.locator.locate(new jsts.triangulate.quadedge.Vertex(p0));if(e===null){return null;} +base=e;if(e.dest().getCoordinate().equals2D(p0)){base=e.sym();} +locEdge=base;do{if(locEdge.dest().getCoordinate().equals2D(p1)){return locEdge;} +locEdge=locEdge.oNext();}while(locEdge!=base);return null;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.insertSite=function(v){var e,base,startEdge;e=this.locate(v);if((v.equals(e.orig(),this.tolerance))||(v.equals(e.dest(),this.tolerance))){return e;} +base=this.makeEdge(e.orig(),v);jsts.triangulate.quadedge.QuadEdge.splice(base,e);startEdge=base;do{base=this.connect(e,base.sym());e=base.oPrev();}while(e.lNext()!=startEdge);return startEdge;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.isFrameEdge=function(e){if(this.isFrameVertex(e.orig())||this.isFrameVertex(e.dest())){return true;} +return false;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.isFrameBorderEdge=function(e){var leftTri,rightTri,vLeftTriOther,vRightTriOther;leftTri=new Array(3);this.getTriangleEdges(e,leftTri);rightTri=new Array(3);this.getTriangleEdges(e.sym(),rightTri);vLeftTriOther=e.lNext().dest();if(this.isFrameVertex(vLeftTriOther)){return true;} +vRightTriOther=e.sym().lNext().dest();if(this.isFrameVertex(vRightTriOther)){return true;} +return false;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.isFrameVertex=function(v){if(v.equals(this.frameVertex[0])){return true;} +if(v.equals(this.frameVertex[1])){return true;} +if(v.equals(this.frameVertex[2])){return true;} +return false;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.isOnEdge=function(e,p){this.seg.setCoordinates(e.orig().getCoordinate(),e.dest().getCoordinate());var dist=this.seg.distance(p);return dist0){edge=edgeStack.pop();if(visitedEdges.indexOf(edge)===-1){priQE=edge.getPrimary();if(includeFrame||!this.isFrameEdge(priQE)){edges.push(priQE);} +edgeStack.push(edge.oNext());edgeStack.push(edge.sym().oNext());visitedEdges.push(edge);visitedEdges.push(edge.sym());}} +return edges;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.visitTriangles=function(triVisitor,includeFrame){this.visitedKey++;var edgeStack,visitedEdges,edge,triEdges;edgeStack=[];edgeStack.push(this.startingEdge);visitedEdges=[];while(edgeStack.length>0){edge=edgeStack.pop();if(visitedEdges.indexOf(edge)===-1){triEdges=this.fetchTriangleToVisit(edge,edgeStack,includeFrame,visitedEdges);if(triEdges!==null) +triVisitor.visit(triEdges);}}};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.fetchTriangleToVisit=function(edge,edgeStack,includeFrame,visitedEdges){var curr,edgeCount,isFrame,sym;curr=edge;edgeCount=0;isFrame=false;do{this.triEdges[edgeCount]=curr;if(this.isFrameEdge(curr)){isFrame=true;} +sym=curr.sym();if(visitedEdges.indexOf(sym)===-1){edgeStack.push(sym);} +visitedEdges.push(curr);edgeCount++;curr=curr.lNext();}while(curr!==edge);if(isFrame&&!includeFrame){return null;} +return this.triEdges;};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.getTriangleEdges=function(includeFrame){var visitor=new jsts.triangulate.quadedge.TriangleEdgesListVisitor();this.visitTriangles(visitor,includeFrame);return visitor.getTriangleEdges();};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.getTriangleVertices=function(includeFrame){var visitor=new TriangleVertexListVisitor();this.visitTriangles(visitor,includeFrame);return visitor.getTriangleVertices();};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.getTriangleCoordinates=function(includeFrame){var visitor=new jsts.triangulate.quadedge.TriangleCoordinatesVisitor();this.visitTriangles(visitor,includeFrame);return visitor.getTriangles();};jsts.triangulate.quadedge.QuadEdgeSubdivision.prototype.getEdgesByFactory=function(geomFact){var quadEdges,edges,i,il,qe,coords;quadEdges=this.getPrimaryEdges(false);edges=[];i=0;il=quadEdges.length;for(i;i0){this.coordList.closeRing();pts=this.coordList.toArray();if(pts.length!==4){return;} +this.triCoords.push(pts);}};jsts.triangulate.quadedge.TriangleCoordinatesVisitor.prototype.getTriangles=function(){return this.triCoords;};jsts.index.kdtree.KdTree=function(tolerance){var tol=0.0;if(tolerance!==undefined){tol=tolerance;} +this.root=null;this.last=null;this.numberOfNodes=0;this.tolerance=tol;};jsts.index.kdtree.KdTree.prototype.insert=function(){if(arguments.length===1){return this.insertCoordinate.apply(this,arguments[0]);}else{return this.insertWithData.apply(this,arguments[0],arguments[1]);}};jsts.index.kdtree.KdTree.prototype.insertCoordinate=function(p){return this.insertWithData(p,null);};jsts.index.kdtree.KdTree.prototype.insertWithData=function(p,data){if(this.root===null){this.root=new jsts.index.kdtree.KdNode(p,data);return this.root;} +var currentNode=this.root,leafNode=this.root,isOddLevel=true,isLessThan=true;while(currentNode!==last){if(isOddLevel){isLessThan=p.xmaxLen){maxLen=lenBC;} +if(lenCA>maxLen){maxLen=lenCA;} +return maxLen;};jsts.geom.Triangle.angleBisector=function(a,b,c){var len0,len2,frac,dx,dy,splitPt;len0=b.distance(a);len2=b.distance(c);frac=len0/(len0+len2);dx=c.x-a.x;dy=c.y-a.y;splitPt=new jsts.geom.Coordinate(a.x+frac*dx,a.y+frac*dy);return splitPt;};jsts.geom.Triangle.area=function(a,b,c){return Math.abs(((c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y))/2.0);};jsts.geom.Triangle.signedArea=function(a,b,c){return((c.x-a.x)*(b.y-a.y)-(b.x-a.x)*(c.y-a.y))/2.0;};jsts.geom.Triangle.prototype.inCentre=function(){return jsts.geom.Triangle.inCentre(this.p0,this.p1,this.p2);};jsts.algorithm.CentroidArea=function(){this.basePt=null;this.triangleCent3=new jsts.geom.Coordinate();this.centSum=new jsts.geom.Coordinate();this.cg3=new jsts.geom.Coordinate();};jsts.algorithm.CentroidArea.prototype.basePt=null;jsts.algorithm.CentroidArea.prototype.triangleCent3=null;jsts.algorithm.CentroidArea.prototype.areasum2=0;jsts.algorithm.CentroidArea.prototype.cg3=null;jsts.algorithm.CentroidArea.prototype.centSum=null;jsts.algorithm.CentroidArea.prototype.totalLength=0.0;jsts.algorithm.CentroidArea.prototype.add=function(geom){if(geom instanceof jsts.geom.Polygon){var poly=geom;this.setBasePoint(poly.getExteriorRing().getCoordinateN(0));this.add3(poly);}else if(geom instanceof jsts.geom.GeometryCollection||geom instanceof jsts.geom.MultiPolygon){var gc=geom;for(var i=0;i0.0){cent.x=this.cg3.x/3/this.areasum2;cent.y=this.cg3.y/3/this.areasum2;}else{cent.x=this.centSum.x/this.totalLength;cent.y=this.centSum.y/this.totalLength;} +return cent;};jsts.algorithm.CentroidArea.prototype.setBasePoint=function(basePt){if(this.basePt==null) +this.basePt=basePt;};jsts.algorithm.CentroidArea.prototype.add3=function(poly){this.addShell(poly.getExteriorRing().getCoordinates());for(var i=0;i0){avg.x/=n;avg.y/=n;} +return avg;};jsts.algorithm.CentralEndpointIntersector.prototype.findNearestPoint=function(p,pts){var minDist=Number.MAX_VALUE;var i,result=null,dist;for(i=0;i=0;i--){this.addPt(pt[i]);}}};jsts.operation.buffer.OffsetSegmentString.prototype.isRedundant=function(pt){if(this.ptList.length<1) +return false;var lastPt=this.ptList[this.ptList.length-1];var ptDist=pt.distance(lastPt);if(ptDist=2) +last2Pt=this.ptList[this.ptList.length-2];if(startPt.equals(lastPt)) +return;this.ptList.push(startPt);};jsts.operation.buffer.OffsetSegmentString.prototype.reverse=function(){};jsts.operation.buffer.OffsetSegmentString.prototype.getCoordinates=function(){return this.ptList;};(function(){var ArrayList=javascript.util.ArrayList;var TreeSet=javascript.util.TreeSet;var CoordinateFilter=jsts.geom.CoordinateFilter;jsts.util.UniqueCoordinateArrayFilter=function(){this.treeSet=new TreeSet();this.list=new ArrayList();};jsts.util.UniqueCoordinateArrayFilter.prototype=new CoordinateFilter();jsts.util.UniqueCoordinateArrayFilter.prototype.treeSet=null;jsts.util.UniqueCoordinateArrayFilter.prototype.list=null;jsts.util.UniqueCoordinateArrayFilter.prototype.getCoordinates=function(){return this.list.toArray();};jsts.util.UniqueCoordinateArrayFilter.prototype.filter=function(coord){if(!this.treeSet.contains(coord)){this.list.add(coord);this.treeSet.add(coord);}};})();(function(){var CGAlgorithms=jsts.algorithm.CGAlgorithms;var UniqueCoordinateArrayFilter=jsts.util.UniqueCoordinateArrayFilter;var Assert=jsts.util.Assert;var Stack=javascript.util.Stack;var ArrayList=javascript.util.ArrayList;var Arrays=javascript.util.Arrays;var RadialComparator=function(origin){this.origin=origin;};RadialComparator.prototype.origin=null;RadialComparator.prototype.compare=function(o1,o2){var p1=o1;var p2=o2;return RadialComparator.polarCompare(this.origin,p1,p2);};RadialComparator.polarCompare=function(o,p,q){var dxp=p.x-o.x;var dyp=p.y-o.y;var dxq=q.x-o.x;var dyq=q.y-o.y;var orient=CGAlgorithms.computeOrientation(o,p,q);if(orient==CGAlgorithms.COUNTERCLOCKWISE) +return 1;if(orient==CGAlgorithms.CLOCKWISE) +return-1;var op=dxp*dxp+dyp*dyp;var oq=dxq*dxq+dyq*dyq;if(opoq){return 1;} +return 0;};jsts.algorithm.ConvexHull=function(){if(arguments.length===1){var geometry=arguments[0];this.inputPts=jsts.algorithm.ConvexHull.extractCoordinates(geometry);this.geomFactory=geometry.getFactory();}else{this.pts=arguments[0];this.geomFactory=arguments[1];}};jsts.algorithm.ConvexHull.prototype.geomFactory=null;jsts.algorithm.ConvexHull.prototype.inputPts=null;jsts.algorithm.ConvexHull.extractCoordinates=function(geom){var filter=new UniqueCoordinateArrayFilter();geom.apply(filter);return filter.getCoordinates();};jsts.algorithm.ConvexHull.prototype.getConvexHull=function(){if(this.inputPts.length==0){return this.geomFactory.createGeometryCollection(null);} +if(this.inputPts.length==1){return this.geomFactory.createPoint(this.inputPts[0]);} +if(this.inputPts.length==2){return this.geomFactory.createLineString(this.inputPts);} +var reducedPts=this.inputPts;if(this.inputPts.length>50){reducedPts=this.reduce(this.inputPts);} +var sortedPts=this.preSort(reducedPts);var cHS=this.grahamScan(sortedPts);var cH=cHS.toArray();return this.lineOrPolygon(cH);};jsts.algorithm.ConvexHull.prototype.reduce=function(inputPts){var polyPts=this.computeOctRing(inputPts);if(polyPts==null) +return this.inputPts;var reducedSet=new javascript.util.TreeSet();for(var i=0;i0){p=ps.pop();} +p=ps.push(p);p=ps.push(c[i]);} +p=ps.push(c[0]);return ps;};jsts.algorithm.ConvexHull.prototype.isBetween=function(c1,c2,c3){if(CGAlgorithms.computeOrientation(c1,c2,c3)!==0){return false;} +if(c1.x!=c3.x){if(c1.x<=c2.x&&c2.x<=c3.x){return true;} +if(c3.x<=c2.x&&c2.x<=c1.x){return true;}} +if(c1.y!=c3.y){if(c1.y<=c2.y&&c2.y<=c3.y){return true;} +if(c3.y<=c2.y&&c2.y<=c1.y){return true;}} +return false;};jsts.algorithm.ConvexHull.prototype.computeOctRing=function(inputPts){var octPts=this.computeOctPts(inputPts);var coordList=new jsts.geom.CoordinateList();coordList.add(octPts,false);if(coordList.size()<3){return null;} +coordList.closeRing();return coordList.toCoordinateArray();};jsts.algorithm.ConvexHull.prototype.computeOctPts=function(inputPts){var pts=[];for(var j=0;j<8;j++){pts[j]=inputPts[0];} +for(var i=1;ipts[2].y){pts[2]=inputPts[i];} +if(inputPts[i].x+inputPts[i].y>pts[3].x+pts[3].y){pts[3]=inputPts[i];} +if(inputPts[i].x>pts[4].x){pts[4]=inputPts[i];} +if(inputPts[i].x-inputPts[i].y>pts[5].x-pts[5].y){pts[5]=inputPts[i];} +if(inputPts[i].y=2,'found LineString with single point');this.insertBoundaryPoint(this.argIndex,coord[0]);this.insertBoundaryPoint(this.argIndex,coord[coord.length-1]);};jsts.geomgraph.GeometryGraph.prototype.addPolygonRing=function(lr,cwLeft,cwRight){if(lr.isEmpty()) +return;var coord=jsts.geom.CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());if(coord.length<4){this.hasTooFewPoints=true;this.invalidPoint=coord[0];return;} +var left=cwLeft;var right=cwRight;if(jsts.algorithm.CGAlgorithms.isCCW(coord)){left=cwRight;right=cwLeft;} +var e=new jsts.geomgraph.Edge(coord,new jsts.geomgraph.Label(this.argIndex,Location.BOUNDARY,left,right));this.lineEdgeMap.put(lr,e);this.insertEdge(e);this.insertPoint(this.argIndex,coord[0],Location.BOUNDARY);};jsts.geomgraph.GeometryGraph.prototype.addPolygon=function(p){this.addPolygonRing(p.getExteriorRing(),Location.EXTERIOR,Location.INTERIOR);for(var i=0;ic?b=!0:m[d]=0;b||(p=!1)}function A(a,c,b,d,g){var f,e,h=[],j=b.type;if(!l[a])return[];"keyup"==j&&u(a)&&(c=[a]);for(f=0;fd||h.hasOwnProperty(d)&&(q[h[d]]=d)}b=q[a]?"keydown":"keypress"}"keypress"==b&&c.length&&(b="keydown");return b}function C(a, + c,b,d,g){r[a+":"+b]=c;a=a.replace(/\s+/g," ");var f=a.split(" "),e,h,j=[];if(1":".","?":"/","|":"\\"},E={option:"alt",command:"meta","return":"enter",escape:"esc"},q,l={},r={},m={},D,x=!1,p=!1,g=1;20>g;++g)h[111+g]="f"+g;for(g=0;9>=g;++g)h[g+96]=g;s(document,"keypress",w);s(document,"keydown",w);s(document,"keyup",w);var k={bind:function(a,c,b){a=a instanceof Array?a:[a];for(var d=0;d MIT Licensed */ + +var io = ('undefined' === typeof module ? {} : module.exports); +(function() { + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, global) { + + /** + * IO namespace. + * + * @namespace + */ + + var io = exports; + + /** + * Socket.IO version + * + * @api public + */ + + io.version = '0.9.10'; + + /** + * Protocol implemented. + * + * @api public + */ + + io.protocol = 1; + + /** + * Available transports, these will be populated with the available transports + * + * @api public + */ + + io.transports = []; + + /** + * Keep track of jsonp callbacks. + * + * @api private + */ + + io.j = []; + + /** + * Keep track of our io.Sockets + * + * @api private + */ + io.sockets = {}; + + + /** + * Manages connections to hosts. + * + * @param {String} uri + * @Param {Boolean} force creation of new socket (defaults to false) + * @api public + */ + + io.connect = function (host, details) { + var uri = io.util.parseUri(host) + , uuri + , socket; + + if (global && global.location) { + uri.protocol = uri.protocol || global.location.protocol.slice(0, -1); + uri.host = uri.host || (global.document + ? global.document.domain : global.location.hostname); + uri.port = uri.port || global.location.port; + } + + uuri = io.util.uniqueUri(uri); + + var options = { + host: uri.host + , secure: 'https' == uri.protocol + , port: uri.port || ('https' == uri.protocol ? 443 : 80) + , query: uri.query || '' + }; + + io.util.merge(options, details); + + if (options['force new connection'] || !io.sockets[uuri]) { + socket = new io.Socket(options); + } + + if (!options['force new connection'] && socket) { + io.sockets[uuri] = socket; + } + + socket = socket || io.sockets[uuri]; + + // if path is different from '' or / + return socket.of(uri.path.length > 1 ? uri.path : ''); + }; + +})('object' === typeof module ? module.exports : (this.io = {}), this); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, global) { + + /** + * Utilities namespace. + * + * @namespace + */ + + var util = exports.util = {}; + + /** + * Parses an URI + * + * @author Steven Levithan (MIT license) + * @api public + */ + + var re = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; + + var parts = ['source', 'protocol', 'authority', 'userInfo', 'user', 'password', + 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', + 'anchor']; + + util.parseUri = function (str) { + var m = re.exec(str || '') + , uri = {} + , i = 14; + + while (i--) { + uri[parts[i]] = m[i] || ''; + } + + return uri; + }; + + /** + * Produces a unique url that identifies a Socket.IO connection. + * + * @param {Object} uri + * @api public + */ + + util.uniqueUri = function (uri) { + var protocol = uri.protocol + , host = uri.host + , port = uri.port; + + if ('document' in global) { + host = host || document.domain; + port = port || (protocol == 'https' + && document.location.protocol !== 'https:' ? 443 : document.location.port); + } else { + host = host || 'localhost'; + + if (!port && protocol == 'https') { + port = 443; + } + } + + return (protocol || 'http') + '://' + host + ':' + (port || 80); + }; + + /** + * Mergest 2 query strings in to once unique query string + * + * @param {String} base + * @param {String} addition + * @api public + */ + + util.query = function (base, addition) { + var query = util.chunkQuery(base || '') + , components = []; + + util.merge(query, util.chunkQuery(addition || '')); + for (var part in query) { + if (query.hasOwnProperty(part)) { + components.push(part + '=' + query[part]); + } + } + + return components.length ? '?' + components.join('&') : ''; + }; + + /** + * Transforms a querystring in to an object + * + * @param {String} qs + * @api public + */ + + util.chunkQuery = function (qs) { + var query = {} + , params = qs.split('&') + , i = 0 + , l = params.length + , kv; + + for (; i < l; ++i) { + kv = params[i].split('='); + if (kv[0]) { + query[kv[0]] = kv[1]; + } + } + + return query; + }; + + /** + * Executes the given function when the page is loaded. + * + * io.util.load(function () { console.log('page loaded'); }); + * + * @param {Function} fn + * @api public + */ + + var pageLoaded = false; + + util.load = function (fn) { + if ('document' in global && document.readyState === 'complete' || pageLoaded) { + return fn(); + } + + util.on(global, 'load', fn, false); + }; + + /** + * Adds an event. + * + * @api private + */ + + util.on = function (element, event, fn, capture) { + if (element.attachEvent) { + element.attachEvent('on' + event, fn); + } else if (element.addEventListener) { + element.addEventListener(event, fn, capture); + } + }; + + /** + * Generates the correct `XMLHttpRequest` for regular and cross domain requests. + * + * @param {Boolean} [xdomain] Create a request that can be used cross domain. + * @returns {XMLHttpRequest|false} If we can create a XMLHttpRequest. + * @api private + */ + + util.request = function (xdomain) { + + if (xdomain && 'undefined' != typeof XDomainRequest) { + return new XDomainRequest(); + } + + if ('undefined' != typeof XMLHttpRequest && (!xdomain || util.ua.hasCORS)) { + return new XMLHttpRequest(); + } + + if (!xdomain) { + try { + return new window[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP'); + } catch(e) { } + } + + return null; + }; + + /** + * XHR based transport constructor. + * + * @constructor + * @api public + */ + + /** + * Change the internal pageLoaded value. + */ + + if ('undefined' != typeof window) { + util.load(function () { + pageLoaded = true; + }); + } + + /** + * Defers a function to ensure a spinner is not displayed by the browser + * + * @param {Function} fn + * @api public + */ + + util.defer = function (fn) { + if (!util.ua.webkit || 'undefined' != typeof importScripts) { + return fn(); + } + + util.load(function () { + setTimeout(fn, 100); + }); + }; + + /** + * Merges two objects. + * + * @api public + */ + + util.merge = function merge (target, additional, deep, lastseen) { + var seen = lastseen || [] + , depth = typeof deep == 'undefined' ? 2 : deep + , prop; + + for (prop in additional) { + if (additional.hasOwnProperty(prop) && util.indexOf(seen, prop) < 0) { + if (typeof target[prop] !== 'object' || !depth) { + target[prop] = additional[prop]; + seen.push(additional[prop]); + } else { + util.merge(target[prop], additional[prop], depth - 1, seen); + } + } + } + + return target; + }; + + /** + * Merges prototypes from objects + * + * @api public + */ + + util.mixin = function (ctor, ctor2) { + util.merge(ctor.prototype, ctor2.prototype); + }; + + /** + * Shortcut for prototypical and static inheritance. + * + * @api private + */ + + util.inherit = function (ctor, ctor2) { + function f() {}; + f.prototype = ctor2.prototype; + ctor.prototype = new f; + }; + + /** + * Checks if the given object is an Array. + * + * io.util.isArray([]); // true + * io.util.isArray({}); // false + * + * @param Object obj + * @api public + */ + + util.isArray = Array.isArray || function (obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; + }; + + /** + * Intersects values of two arrays into a third + * + * @api public + */ + + util.intersect = function (arr, arr2) { + var ret = [] + , longest = arr.length > arr2.length ? arr : arr2 + , shortest = arr.length > arr2.length ? arr2 : arr; + + for (var i = 0, l = shortest.length; i < l; i++) { + if (~util.indexOf(longest, shortest[i])) + ret.push(shortest[i]); + } + + return ret; + } + + /** + * Array indexOf compatibility. + * + * @see bit.ly/a5Dxa2 + * @api public + */ + + util.indexOf = function (arr, o, i) { + + for (var j = arr.length, i = i < 0 ? i + j < 0 ? 0 : i + j : i || 0; + i < j && arr[i] !== o; i++) {} + + return j <= i ? -1 : i; + }; + + /** + * Converts enumerables to array. + * + * @api public + */ + + util.toArray = function (enu) { + var arr = []; + + for (var i = 0, l = enu.length; i < l; i++) + arr.push(enu[i]); + + return arr; + }; + + /** + * UA / engines detection namespace. + * + * @namespace + */ + + util.ua = {}; + + /** + * Whether the UA supports CORS for XHR. + * + * @api public + */ + + util.ua.hasCORS = 'undefined' != typeof XMLHttpRequest && (function () { + try { + var a = new XMLHttpRequest(); + } catch (e) { + return false; + } + + return a.withCredentials != undefined; + })(); + + /** + * Detect webkit. + * + * @api public + */ + + util.ua.webkit = 'undefined' != typeof navigator + && /webkit/i.test(navigator.userAgent); + + /** + * Detect iPad/iPhone/iPod. + * + * @api public + */ + + util.ua.iDevice = 'undefined' != typeof navigator + && /iPad|iPhone|iPod/i.test(navigator.userAgent); + +})('undefined' != typeof io ? io : module.exports, this); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.EventEmitter = EventEmitter; + + /** + * Event emitter constructor. + * + * @api public. + */ + + function EventEmitter () {}; + + /** + * Adds a listener + * + * @api public + */ + + EventEmitter.prototype.on = function (name, fn) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = fn; + } else if (io.util.isArray(this.$events[name])) { + this.$events[name].push(fn); + } else { + this.$events[name] = [this.$events[name], fn]; + } + + return this; + }; + + EventEmitter.prototype.addListener = EventEmitter.prototype.on; + + /** + * Adds a volatile listener. + * + * @api public + */ + + EventEmitter.prototype.once = function (name, fn) { + var self = this; + + function on () { + self.removeListener(name, on); + fn.apply(this, arguments); + }; + + on.listener = fn; + this.on(name, on); + + return this; + }; + + /** + * Removes a listener. + * + * @api public + */ + + EventEmitter.prototype.removeListener = function (name, fn) { + if (this.$events && this.$events[name]) { + var list = this.$events[name]; + + if (io.util.isArray(list)) { + var pos = -1; + + for (var i = 0, l = list.length; i < l; i++) { + if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { + pos = i; + break; + } + } + + if (pos < 0) { + return this; + } + + list.splice(pos, 1); + + if (!list.length) { + delete this.$events[name]; + } + } else if (list === fn || (list.listener && list.listener === fn)) { + delete this.$events[name]; + } + } + + return this; + }; + + /** + * Removes all listeners for an event. + * + * @api public + */ + + EventEmitter.prototype.removeAllListeners = function (name) { + if (name === undefined) { + this.$events = {}; + return this; + } + + if (this.$events && this.$events[name]) { + this.$events[name] = null; + } + + return this; + }; + + /** + * Gets all listeners for a certain event. + * + * @api publci + */ + + EventEmitter.prototype.listeners = function (name) { + if (!this.$events) { + this.$events = {}; + } + + if (!this.$events[name]) { + this.$events[name] = []; + } + + if (!io.util.isArray(this.$events[name])) { + this.$events[name] = [this.$events[name]]; + } + + return this.$events[name]; + }; + + /** + * Emits an event. + * + * @api public + */ + + EventEmitter.prototype.emit = function (name) { + if (!this.$events) { + return false; + } + + var handler = this.$events[name]; + + if (!handler) { + return false; + } + + var args = Array.prototype.slice.call(arguments, 1); + + if ('function' == typeof handler) { + handler.apply(this, args); + } else if (io.util.isArray(handler)) { + var listeners = handler.slice(); + + for (var i = 0, l = listeners.length; i < l; i++) { + listeners[i].apply(this, args); + } + } else { + return false; + } + + return true; + }; + +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +/** + * Based on JSON2 (http://www.JSON.org/js.html). + */ + +(function (exports, nativeJSON) { + "use strict"; + + // use native JSON if it's available + if (nativeJSON && nativeJSON.parse){ + return exports.JSON = { + parse: nativeJSON.parse + , stringify: nativeJSON.stringify + } + } + + var JSON = exports.JSON = {}; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; + } + + function date(d, key) { + return isFinite(d.valueOf()) ? + d.getUTCFullYear() + '-' + + f(d.getUTCMonth() + 1) + '-' + + f(d.getUTCDate()) + 'T' + + f(d.getUTCHours()) + ':' + + f(d.getUTCMinutes()) + ':' + + f(d.getUTCSeconds()) + 'Z' : null; + }; + + var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, + gap, + indent, + meta = { // table of character substitutions + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }, + rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + escapable.lastIndex = 0; + return escapable.test(string) ? '"' + string.replace(escapable, function (a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' : '"' + string + '"'; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i, // The loop counter. + k, // The member key. + v, // The member value. + length, + mind = gap, + partial, + value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value instanceof Date) { + value = date(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === 'function') { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case 'string': + return quote(value); + + case 'number': + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) ? String(value) : 'null'; + + case 'boolean': + case 'null': + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce 'null'. The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is 'object', we might be dealing with an object or an array or +// null. + + case 'object': + +// Due to a specification blunder in ECMAScript, typeof null is 'object', +// so watch out for that case. + + if (!value) { + return 'null'; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === '[object Array]') { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || 'null'; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 ? '[]' : gap ? + '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : + '[' + partial.join(',') + ']'; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === 'object') { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === 'string') { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + (gap ? ': ' : ':') + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 ? '{}' : gap ? + '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : + '{' + partial.join(',') + '}'; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ''; + indent = ''; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === 'number') { + for (i = 0; i < space; i += 1) { + indent += ' '; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === 'string') { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== 'function' && + (typeof replacer !== 'object' || + typeof replacer.length !== 'number')) { + throw new Error('JSON.stringify'); + } + +// Make a fake root object containing our value under the key of ''. +// Return the result of stringifying the value. + + return str('', {'': value}); + }; + +// If the JSON object does not yet have a parse method, give it one. + + JSON.parse = function (text, reviver) { + // The parse method takes a text and an optional reviver function, and returns + // a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + + // The walk method is used to recursively walk the resulting structure so + // that modifications can be made. + + var k, v, value = holder[key]; + if (value && typeof value === 'object') { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + + // Parsing happens in four stages. In the first stage, we replace certain + // Unicode characters with escape sequences. JavaScript handles many characters + // incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + cx.lastIndex = 0; + if (cx.test(text)) { + text = text.replace(cx, function (a) { + return '\\u' + + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + + // In the second stage, we run the text against regular expressions that look + // for non-JSON patterns. We are especially concerned with '()' and 'new' + // because they can cause invocation, and '=' because it can cause mutation. + // But just to be safe, we want to reject all unexpected forms. + + // We split the second stage into 4 regexp operations in order to work around + // crippling inefficiencies in IE's and Safari's regexp engines. First we + // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we + // replace all simple value tokens with ']' characters. Third, we delete all + // open brackets that follow a colon or comma or that begin the text. Finally, + // we look to see that the remaining characters are only whitespace or ']' or + // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. + + if (/^[\],:{}\s]*$/ + .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') + .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') + .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + // In the third stage we use the eval function to compile the text into a + // JavaScript structure. The '{' operator is subject to a syntactic ambiguity + // in JavaScript: it can begin a block or an object literal. We wrap the text + // in parens to eliminate the ambiguity. + + j = eval('(' + text + ')'); + + // In the optional fourth stage, we recursively walk the new structure, passing + // each name/value pair to a reviver function for possible transformation. + + return typeof reviver === 'function' ? + walk({'': j}, '') : j; + } + + // If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError('JSON.parse'); + }; + +})( + 'undefined' != typeof io ? io : module.exports + , typeof JSON !== 'undefined' ? JSON : undefined +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Parser namespace. + * + * @namespace + */ + + var parser = exports.parser = {}; + + /** + * Packet types. + */ + + var packets = parser.packets = [ + 'disconnect' + , 'connect' + , 'heartbeat' + , 'message' + , 'json' + , 'event' + , 'ack' + , 'error' + , 'noop' + ]; + + /** + * Errors reasons. + */ + + var reasons = parser.reasons = [ + 'transport not supported' + , 'client not handshaken' + , 'unauthorized' + ]; + + /** + * Errors advice. + */ + + var advice = parser.advice = [ + 'reconnect' + ]; + + /** + * Shortcuts. + */ + + var JSON = io.JSON + , indexOf = io.util.indexOf; + + /** + * Encodes a packet. + * + * @api private + */ + + parser.encodePacket = function (packet) { + var type = indexOf(packets, packet.type) + , id = packet.id || '' + , endpoint = packet.endpoint || '' + , ack = packet.ack + , data = null; + + switch (packet.type) { + case 'error': + var reason = packet.reason ? indexOf(reasons, packet.reason) : '' + , adv = packet.advice ? indexOf(advice, packet.advice) : ''; + + if (reason !== '' || adv !== '') + data = reason + (adv !== '' ? ('+' + adv) : ''); + + break; + + case 'message': + if (packet.data !== '') + data = packet.data; + break; + + case 'event': + var ev = { name: packet.name }; + + if (packet.args && packet.args.length) { + ev.args = packet.args; + } + + data = JSON.stringify(ev); + break; + + case 'json': + data = JSON.stringify(packet.data); + break; + + case 'connect': + if (packet.qs) + data = packet.qs; + break; + + case 'ack': + data = packet.ackId + + (packet.args && packet.args.length + ? '+' + JSON.stringify(packet.args) : ''); + break; + } + + // construct packet with required fragments + var encoded = [ + type + , id + (ack == 'data' ? '+' : '') + , endpoint + ]; + + // data fragment is optional + if (data !== null && data !== undefined) + encoded.push(data); + + return encoded.join(':'); + }; + + /** + * Encodes multiple messages (payload). + * + * @param {Array} messages + * @api private + */ + + parser.encodePayload = function (packets) { + var decoded = ''; + + if (packets.length == 1) + return packets[0]; + + for (var i = 0, l = packets.length; i < l; i++) { + var packet = packets[i]; + decoded += '\ufffd' + packet.length + '\ufffd' + packets[i]; + } + + return decoded; + }; + + /** + * Decodes a packet + * + * @api private + */ + + var regexp = /([^:]+):([0-9]+)?(\+)?:([^:]+)?:?([\s\S]*)?/; + + parser.decodePacket = function (data) { + var pieces = data.match(regexp); + + if (!pieces) return {}; + + var id = pieces[2] || '' + , data = pieces[5] || '' + , packet = { + type: packets[pieces[1]] + , endpoint: pieces[4] || '' + }; + + // whether we need to acknowledge the packet + if (id) { + packet.id = id; + if (pieces[3]) + packet.ack = 'data'; + else + packet.ack = true; + } + + // handle different packet types + switch (packet.type) { + case 'error': + var pieces = data.split('+'); + packet.reason = reasons[pieces[0]] || ''; + packet.advice = advice[pieces[1]] || ''; + break; + + case 'message': + packet.data = data || ''; + break; + + case 'event': + try { + var opts = JSON.parse(data); + packet.name = opts.name; + packet.args = opts.args; + } catch (e) { } + + packet.args = packet.args || []; + break; + + case 'json': + try { + packet.data = JSON.parse(data); + } catch (e) { } + break; + + case 'connect': + packet.qs = data || ''; + break; + + case 'ack': + var pieces = data.match(/^([0-9]+)(\+)?(.*)/); + if (pieces) { + packet.ackId = pieces[1]; + packet.args = []; + + if (pieces[3]) { + try { + packet.args = pieces[3] ? JSON.parse(pieces[3]) : []; + } catch (e) { } + } + } + break; + + case 'disconnect': + case 'heartbeat': + break; + }; + + return packet; + }; + + /** + * Decodes data payload. Detects multiple messages + * + * @return {Array} messages + * @api public + */ + + parser.decodePayload = function (data) { + // IE doesn't like data[i] for unicode chars, charAt works fine + if (data.charAt(0) == '\ufffd') { + var ret = []; + + for (var i = 1, length = ''; i < data.length; i++) { + if (data.charAt(i) == '\ufffd') { + ret.push(parser.decodePacket(data.substr(i + 1).substr(0, length))); + i += Number(length) + 1; + length = ''; + } else { + length += data.charAt(i); + } + } + + return ret; + } else { + return [parser.decodePacket(data)]; + } + }; + +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.Transport = Transport; + + /** + * This is the transport template for all supported transport methods. + * + * @constructor + * @api public + */ + + function Transport (socket, sessid) { + this.socket = socket; + this.sessid = sessid; + }; + + /** + * Apply EventEmitter mixin. + */ + + io.util.mixin(Transport, io.EventEmitter); + + + /** + * Indicates whether heartbeats is enabled for this transport + * + * @api private + */ + + Transport.prototype.heartbeats = function () { + return true; + } + + /** + * Handles the response from the server. When a new response is received + * it will automatically update the timeout, decode the message and + * forwards the response to the onMessage function for further processing. + * + * @param {String} data Response from the server. + * @api private + */ + + Transport.prototype.onData = function (data) { + this.clearCloseTimeout(); + + // If the connection in currently open (or in a reopening state) reset the close + // timeout since we have just received data. This check is necessary so + // that we don't reset the timeout on an explicitly disconnected connection. + if (this.socket.connected || this.socket.connecting || this.socket.reconnecting) { + this.setCloseTimeout(); + } + + if (data !== '') { + // todo: we should only do decodePayload for xhr transports + var msgs = io.parser.decodePayload(data); + + if (msgs && msgs.length) { + for (var i = 0, l = msgs.length; i < l; i++) { + this.onPacket(msgs[i]); + } + } + } + + return this; + }; + + /** + * Handles packets. + * + * @api private + */ + + Transport.prototype.onPacket = function (packet) { + this.socket.setHeartbeatTimeout(); + + if (packet.type == 'heartbeat') { + return this.onHeartbeat(); + } + + if (packet.type == 'connect' && packet.endpoint == '') { + this.onConnect(); + } + + if (packet.type == 'error' && packet.advice == 'reconnect') { + this.isOpen = false; + } + + this.socket.onPacket(packet); + + return this; + }; + + /** + * Sets close timeout + * + * @api private + */ + + Transport.prototype.setCloseTimeout = function () { + if (!this.closeTimeout) { + var self = this; + + this.closeTimeout = setTimeout(function () { + self.onDisconnect(); + }, this.socket.closeTimeout); + } + }; + + /** + * Called when transport disconnects. + * + * @api private + */ + + Transport.prototype.onDisconnect = function () { + if (this.isOpen) this.close(); + this.clearTimeouts(); + this.socket.onDisconnect(); + return this; + }; + + /** + * Called when transport connects + * + * @api private + */ + + Transport.prototype.onConnect = function () { + this.socket.onConnect(); + return this; + } + + /** + * Clears close timeout + * + * @api private + */ + + Transport.prototype.clearCloseTimeout = function () { + if (this.closeTimeout) { + clearTimeout(this.closeTimeout); + this.closeTimeout = null; + } + }; + + /** + * Clear timeouts + * + * @api private + */ + + Transport.prototype.clearTimeouts = function () { + this.clearCloseTimeout(); + + if (this.reopenTimeout) { + clearTimeout(this.reopenTimeout); + } + }; + + /** + * Sends a packet + * + * @param {Object} packet object. + * @api private + */ + + Transport.prototype.packet = function (packet) { + this.send(io.parser.encodePacket(packet)); + }; + + /** + * Send the received heartbeat message back to server. So the server + * knows we are still connected. + * + * @param {String} heartbeat Heartbeat response from the server. + * @api private + */ + + Transport.prototype.onHeartbeat = function (heartbeat) { + this.packet({ type: 'heartbeat' }); + }; + + /** + * Called when the transport opens. + * + * @api private + */ + + Transport.prototype.onOpen = function () { + this.isOpen = true; + this.clearCloseTimeout(); + this.socket.onOpen(); + }; + + /** + * Notifies the base when the connection with the Socket.IO server + * has been disconnected. + * + * @api private + */ + + Transport.prototype.onClose = function () { + var self = this; + + /* FIXME: reopen delay causing a infinit loop + this.reopenTimeout = setTimeout(function () { + self.open(); + }, this.socket.options['reopen delay']);*/ + + this.isOpen = false; + this.socket.onClose(); + this.onDisconnect(); + }; + + /** + * Generates a connection url based on the Socket.IO URL Protocol. + * See for more details. + * + * @returns {String} Connection url + * @api private + */ + + Transport.prototype.prepareUrl = function () { + var options = this.socket.options; + + return this.scheme() + '://' + + options.host + ':' + options.port + '/' + + options.resource + '/' + io.protocol + + '/' + this.name + '/' + this.sessid; + }; + + /** + * Checks if the transport is ready to start a connection. + * + * @param {Socket} socket The socket instance that needs a transport + * @param {Function} fn The callback + * @api private + */ + + Transport.prototype.ready = function (socket, fn) { + fn.call(this); + }; +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + + /** + * Expose constructor. + */ + + exports.Socket = Socket; + + /** + * Create a new `Socket.IO client` which can establish a persistent + * connection with a Socket.IO enabled server. + * + * @api public + */ + + function Socket (options) { + this.options = { + port: 80 + , secure: false + , document: 'document' in global ? document : false + , resource: 'socket.io' + , transports: io.transports + , 'connect timeout': 10000 + , 'try multiple transports': true + , 'reconnect': true + , 'reconnection delay': 500 + , 'reconnection limit': Infinity + , 'reopen delay': 3000 + , 'max reconnection attempts': 10 + , 'sync disconnect on unload': false + , 'auto connect': true + , 'flash policy port': 10843 + , 'manualFlush': false + }; + + io.util.merge(this.options, options); + + this.connected = false; + this.open = false; + this.connecting = false; + this.reconnecting = false; + this.namespaces = {}; + this.buffer = []; + this.doBuffer = false; + + if (this.options['sync disconnect on unload'] && + (!this.isXDomain() || io.util.ua.hasCORS)) { + var self = this; + io.util.on(global, 'beforeunload', function () { + self.disconnectSync(); + }, false); + } + + if (this.options['auto connect']) { + this.connect(); + } +}; + + /** + * Apply EventEmitter mixin. + */ + + io.util.mixin(Socket, io.EventEmitter); + + /** + * Returns a namespace listener/emitter for this socket + * + * @api public + */ + + Socket.prototype.of = function (name) { + if (!this.namespaces[name]) { + this.namespaces[name] = new io.SocketNamespace(this, name); + + if (name !== '') { + this.namespaces[name].packet({ type: 'connect' }); + } + } + + return this.namespaces[name]; + }; + + /** + * Emits the given event to the Socket and all namespaces + * + * @api private + */ + + Socket.prototype.publish = function () { + this.emit.apply(this, arguments); + + var nsp; + + for (var i in this.namespaces) { + if (this.namespaces.hasOwnProperty(i)) { + nsp = this.of(i); + nsp.$emit.apply(nsp, arguments); + } + } + }; + + /** + * Performs the handshake + * + * @api private + */ + + function empty () { }; + + Socket.prototype.handshake = function (fn) { + var self = this + , options = this.options; + + function complete (data) { + if (data instanceof Error) { + self.connecting = false; + self.onError(data.message); + } else { + fn.apply(null, data.split(':')); + } + }; + + var url = [ + 'http' + (options.secure ? 's' : '') + ':/' + , options.host + ':' + options.port + , options.resource + , io.protocol + , io.util.query(this.options.query, 't=' + +new Date) + ].join('/'); + + if (this.isXDomain() && !io.util.ua.hasCORS) { + var insertAt = document.getElementsByTagName('script')[0] + , script = document.createElement('script'); + + script.src = url + '&jsonp=' + io.j.length; + insertAt.parentNode.insertBefore(script, insertAt); + + io.j.push(function (data) { + complete(data); + script.parentNode.removeChild(script); + }); + } else { + var xhr = io.util.request(); + + xhr.open('GET', url, true); + if (this.isXDomain()) { + xhr.withCredentials = true; + } + xhr.onreadystatechange = function () { + if (xhr.readyState == 4) { + xhr.onreadystatechange = empty; + + if (xhr.status == 200) { + complete(xhr.responseText); + } else if (xhr.status == 403) { + self.onError(xhr.responseText); + } else { + self.connecting = false; + !self.reconnecting && self.onError(xhr.responseText); + } + } + }; + xhr.send(null); + } + }; + + /** + * Find an available transport based on the options supplied in the constructor. + * + * @api private + */ + + Socket.prototype.getTransport = function (override) { + var transports = override || this.transports, match; + + for (var i = 0, transport; transport = transports[i]; i++) { + if (io.Transport[transport] + && io.Transport[transport].check(this) + && (!this.isXDomain() || io.Transport[transport].xdomainCheck(this))) { + return new io.Transport[transport](this, this.sessionid); + } + } + + return null; + }; + + /** + * Connects to the server. + * + * @param {Function} [fn] Callback. + * @returns {io.Socket} + * @api public + */ + + Socket.prototype.connect = function (fn) { + if (this.connecting) { + return this; + } + + var self = this; + self.connecting = true; + + this.handshake(function (sid, heartbeat, close, transports) { + self.sessionid = sid; + self.closeTimeout = close * 1000; + self.heartbeatTimeout = heartbeat * 1000; + if(!self.transports) + self.transports = self.origTransports = (transports ? io.util.intersect( + transports.split(',') + , self.options.transports + ) : self.options.transports); + + self.setHeartbeatTimeout(); + + function connect (transports){ + if (self.transport) self.transport.clearTimeouts(); + + self.transport = self.getTransport(transports); + if (!self.transport) return self.publish('connect_failed'); + + // once the transport is ready + self.transport.ready(self, function () { + self.connecting = true; + self.publish('connecting', self.transport.name); + self.transport.open(); + + if (self.options['connect timeout']) { + self.connectTimeoutTimer = setTimeout(function () { + if (!self.connected) { + self.connecting = false; + + if (self.options['try multiple transports']) { + var remaining = self.transports; + + while (remaining.length > 0 && remaining.splice(0,1)[0] != + self.transport.name) {} + + if (remaining.length){ + connect(remaining); + } else { + self.publish('connect_failed'); + } + } + } + }, self.options['connect timeout']); + } + }); + } + + connect(self.transports); + + self.once('connect', function (){ + clearTimeout(self.connectTimeoutTimer); + + fn && typeof fn == 'function' && fn(); + }); + }); + + return this; + }; + + /** + * Clears and sets a new heartbeat timeout using the value given by the + * server during the handshake. + * + * @api private + */ + + Socket.prototype.setHeartbeatTimeout = function () { + clearTimeout(this.heartbeatTimeoutTimer); + if(this.transport && !this.transport.heartbeats()) return; + + var self = this; + this.heartbeatTimeoutTimer = setTimeout(function () { + self.transport.onClose(); + }, this.heartbeatTimeout); + }; + + /** + * Sends a message. + * + * @param {Object} data packet. + * @returns {io.Socket} + * @api public + */ + + Socket.prototype.packet = function (data) { + if (this.connected && !this.doBuffer) { + this.transport.packet(data); + } else { + this.buffer.push(data); + } + + return this; + }; + + /** + * Sets buffer state + * + * @api private + */ + + Socket.prototype.setBuffer = function (v) { + this.doBuffer = v; + + if (!v && this.connected && this.buffer.length) { + if (!this.options['manualFlush']) { + this.flushBuffer(); + } + } + }; + + /** + * Flushes the buffer data over the wire. + * To be invoked manually when 'manualFlush' is set to true. + * + * @api public + */ + + Socket.prototype.flushBuffer = function() { + this.transport.payload(this.buffer); + this.buffer = []; + }; + + + /** + * Disconnect the established connect. + * + * @returns {io.Socket} + * @api public + */ + + Socket.prototype.disconnect = function () { + if (this.connected || this.connecting) { + if (this.open) { + this.of('').packet({ type: 'disconnect' }); + } + + // handle disconnection immediately + this.onDisconnect('booted'); + } + + return this; + }; + + /** + * Disconnects the socket with a sync XHR. + * + * @api private + */ + + Socket.prototype.disconnectSync = function () { + // ensure disconnection + var xhr = io.util.request(); + var uri = [ + 'http' + (this.options.secure ? 's' : '') + ':/' + , this.options.host + ':' + this.options.port + , this.options.resource + , io.protocol + , '' + , this.sessionid + ].join('/') + '/?disconnect=1'; + + xhr.open('GET', uri, false); + xhr.send(null); + + // handle disconnection immediately + this.onDisconnect('booted'); + }; + + /** + * Check if we need to use cross domain enabled transports. Cross domain would + * be a different port or different domain name. + * + * @returns {Boolean} + * @api private + */ + + Socket.prototype.isXDomain = function () { + + var port = global.location.port || + ('https:' == global.location.protocol ? 443 : 80); + + return this.options.host !== global.location.hostname + || this.options.port != port; + }; + + /** + * Called upon handshake. + * + * @api private + */ + + Socket.prototype.onConnect = function () { + if (!this.connected) { + this.connected = true; + this.connecting = false; + if (!this.doBuffer) { + // make sure to flush the buffer + this.setBuffer(false); + } + this.emit('connect'); + } + }; + + /** + * Called when the transport opens + * + * @api private + */ + + Socket.prototype.onOpen = function () { + this.open = true; + }; + + /** + * Called when the transport closes. + * + * @api private + */ + + Socket.prototype.onClose = function () { + this.open = false; + clearTimeout(this.heartbeatTimeoutTimer); + }; + + /** + * Called when the transport first opens a connection + * + * @param text + */ + + Socket.prototype.onPacket = function (packet) { + this.of(packet.endpoint).onPacket(packet); + }; + + /** + * Handles an error. + * + * @api private + */ + + Socket.prototype.onError = function (err) { + if (err && err.advice) { + if (err.advice === 'reconnect' && (this.connected || this.connecting)) { + this.disconnect(); + if (this.options.reconnect) { + this.reconnect(); + } + } + } + + this.publish('error', err && err.reason ? err.reason : err); + }; + + /** + * Called when the transport disconnects. + * + * @api private + */ + + Socket.prototype.onDisconnect = function (reason) { + var wasConnected = this.connected + , wasConnecting = this.connecting; + + this.connected = false; + this.connecting = false; + this.open = false; + + if (wasConnected || wasConnecting) { + this.transport.close(); + this.transport.clearTimeouts(); + if (wasConnected) { + this.publish('disconnect', reason); + + if ('booted' != reason && this.options.reconnect && !this.reconnecting) { + this.reconnect(); + } + } + } + }; + + /** + * Called upon reconnection. + * + * @api private + */ + + Socket.prototype.reconnect = function () { + this.reconnecting = true; + this.reconnectionAttempts = 0; + this.reconnectionDelay = this.options['reconnection delay']; + + var self = this + , maxAttempts = this.options['max reconnection attempts'] + , tryMultiple = this.options['try multiple transports'] + , limit = this.options['reconnection limit']; + + function reset () { + if (self.connected) { + for (var i in self.namespaces) { + if (self.namespaces.hasOwnProperty(i) && '' !== i) { + self.namespaces[i].packet({ type: 'connect' }); + } + } + self.publish('reconnect', self.transport.name, self.reconnectionAttempts); + } + + clearTimeout(self.reconnectionTimer); + + self.removeListener('connect_failed', maybeReconnect); + self.removeListener('connect', maybeReconnect); + + self.reconnecting = false; + + delete self.reconnectionAttempts; + delete self.reconnectionDelay; + delete self.reconnectionTimer; + delete self.redoTransports; + + self.options['try multiple transports'] = tryMultiple; + }; + + function maybeReconnect () { + if (!self.reconnecting) { + return; + } + + if (self.connected) { + return reset(); + }; + + if (self.connecting && self.reconnecting) { + return self.reconnectionTimer = setTimeout(maybeReconnect, 1000); + } + + if (self.reconnectionAttempts++ >= maxAttempts) { + if (!self.redoTransports) { + self.on('connect_failed', maybeReconnect); + self.options['try multiple transports'] = true; + self.transports = self.origTransports; + self.transport = self.getTransport(); + self.redoTransports = true; + self.connect(); + } else { + self.publish('reconnect_failed'); + reset(); + } + } else { + if (self.reconnectionDelay < limit) { + self.reconnectionDelay *= 2; // exponential back off + } + + self.connect(); + self.publish('reconnecting', self.reconnectionDelay, self.reconnectionAttempts); + self.reconnectionTimer = setTimeout(maybeReconnect, self.reconnectionDelay); + } + }; + + this.options['try multiple transports'] = false; + this.reconnectionTimer = setTimeout(maybeReconnect, this.reconnectionDelay); + + this.on('connect', maybeReconnect); + }; + +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports + , this +); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.SocketNamespace = SocketNamespace; + + /** + * Socket namespace constructor. + * + * @constructor + * @api public + */ + + function SocketNamespace (socket, name) { + this.socket = socket; + this.name = name || ''; + this.flags = {}; + this.json = new Flag(this, 'json'); + this.ackPackets = 0; + this.acks = {}; + }; + + /** + * Apply EventEmitter mixin. + */ + + io.util.mixin(SocketNamespace, io.EventEmitter); + + /** + * Copies emit since we override it + * + * @api private + */ + + SocketNamespace.prototype.$emit = io.EventEmitter.prototype.emit; + + /** + * Creates a new namespace, by proxying the request to the socket. This + * allows us to use the synax as we do on the server. + * + * @api public + */ + + SocketNamespace.prototype.of = function () { + return this.socket.of.apply(this.socket, arguments); + }; + + /** + * Sends a packet. + * + * @api private + */ + + SocketNamespace.prototype.packet = function (packet) { + packet.endpoint = this.name; + this.socket.packet(packet); + this.flags = {}; + return this; + }; + + /** + * Sends a message + * + * @api public + */ + + SocketNamespace.prototype.send = function (data, fn) { + var packet = { + type: this.flags.json ? 'json' : 'message' + , data: data + }; + + if ('function' == typeof fn) { + packet.id = ++this.ackPackets; + packet.ack = true; + this.acks[packet.id] = fn; + } + + return this.packet(packet); + }; + + /** + * Emits an event + * + * @api public + */ + + SocketNamespace.prototype.emit = function (name) { + var args = Array.prototype.slice.call(arguments, 1) + , lastArg = args[args.length - 1] + , packet = { + type: 'event' + , name: name + }; + + if ('function' == typeof lastArg) { + packet.id = ++this.ackPackets; + packet.ack = 'data'; + this.acks[packet.id] = lastArg; + args = args.slice(0, args.length - 1); + } + + packet.args = args; + + return this.packet(packet); + }; + + /** + * Disconnects the namespace + * + * @api private + */ + + SocketNamespace.prototype.disconnect = function () { + if (this.name === '') { + this.socket.disconnect(); + } else { + this.packet({ type: 'disconnect' }); + this.$emit('disconnect'); + } + + return this; + }; + + /** + * Handles a packet + * + * @api private + */ + + SocketNamespace.prototype.onPacket = function (packet) { + var self = this; + + function ack () { + self.packet({ + type: 'ack' + , args: io.util.toArray(arguments) + , ackId: packet.id + }); + }; + + switch (packet.type) { + case 'connect': + this.$emit('connect'); + break; + + case 'disconnect': + if (this.name === '') { + this.socket.onDisconnect(packet.reason || 'booted'); + } else { + this.$emit('disconnect', packet.reason); + } + break; + + case 'message': + case 'json': + var params = ['message', packet.data]; + + if (packet.ack == 'data') { + params.push(ack); + } else if (packet.ack) { + this.packet({ type: 'ack', ackId: packet.id }); + } + + this.$emit.apply(this, params); + break; + + case 'event': + var params = [packet.name].concat(packet.args); + + if (packet.ack == 'data') + params.push(ack); + + this.$emit.apply(this, params); + break; + + case 'ack': + if (this.acks[packet.ackId]) { + this.acks[packet.ackId].apply(this, packet.args); + delete this.acks[packet.ackId]; + } + break; + + case 'error': + if (packet.advice){ + this.socket.onError(packet); + } else { + if (packet.reason == 'unauthorized') { + this.$emit('connect_failed', packet.reason); + } else { + this.$emit('error', packet.reason); + } + } + break; + } + }; + + /** + * Flag interface. + * + * @api private + */ + + function Flag (nsp, name) { + this.namespace = nsp; + this.name = name; + }; + + /** + * Send a message + * + * @api public + */ + + Flag.prototype.send = function () { + this.namespace.flags[this.name] = true; + this.namespace.send.apply(this.namespace, arguments); + }; + + /** + * Emit an event + * + * @api public + */ + + Flag.prototype.emit = function () { + this.namespace.flags[this.name] = true; + this.namespace.emit.apply(this.namespace, arguments); + }; + +})( + 'undefined' != typeof io ? io : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + + /** + * Expose constructor. + */ + + exports.websocket = WS; + + /** + * The WebSocket transport uses the HTML5 WebSocket API to establish an + * persistent connection with the Socket.IO server. This transport will also + * be inherited by the FlashSocket fallback as it provides a API compatible + * polyfill for the WebSockets. + * + * @constructor + * @extends {io.Transport} + * @api public + */ + + function WS (socket) { + io.Transport.apply(this, arguments); + }; + + /** + * Inherits from Transport. + */ + + io.util.inherit(WS, io.Transport); + + /** + * Transport name + * + * @api public + */ + + WS.prototype.name = 'websocket'; + + /** + * Initializes a new `WebSocket` connection with the Socket.IO server. We attach + * all the appropriate listeners to handle the responses from the server. + * + * @returns {Transport} + * @api public + */ + + WS.prototype.open = function () { + var query = io.util.query(this.socket.options.query) + , self = this + , Socket + + + if (!Socket) { + Socket = global.MozWebSocket || global.WebSocket; + } + + this.websocket = new Socket(this.prepareUrl() + query); + + this.websocket.onopen = function () { + self.onOpen(); + self.socket.setBuffer(false); + }; + this.websocket.onmessage = function (ev) { + self.onData(ev.data); + }; + this.websocket.onclose = function () { + self.onClose(); + self.socket.setBuffer(true); + }; + this.websocket.onerror = function (e) { + self.onError(e); + }; + + return this; + }; + + /** + * Send a message to the Socket.IO server. The message will automatically be + * encoded in the correct message format. + * + * @returns {Transport} + * @api public + */ + + // Do to a bug in the current IDevices browser, we need to wrap the send in a + // setTimeout, when they resume from sleeping the browser will crash if + // we don't allow the browser time to detect the socket has been closed + if (io.util.ua.iDevice) { + WS.prototype.send = function (data) { + var self = this; + setTimeout(function() { + self.websocket.send(data); + },0); + return this; + }; + } else { + WS.prototype.send = function (data) { + this.websocket.send(data); + return this; + }; + } + + /** + * Payload + * + * @api private + */ + + WS.prototype.payload = function (arr) { + for (var i = 0, l = arr.length; i < l; i++) { + this.packet(arr[i]); + } + return this; + }; + + /** + * Disconnect the established `WebSocket` connection. + * + * @returns {Transport} + * @api public + */ + + WS.prototype.close = function () { + this.websocket.close(); + return this; + }; + + /** + * Handle the errors that `WebSocket` might be giving when we + * are attempting to connect or send messages. + * + * @param {Error} e The error. + * @api private + */ + + WS.prototype.onError = function (e) { + this.socket.onError(e); + }; + + /** + * Returns the appropriate scheme for the URI generation. + * + * @api private + */ + WS.prototype.scheme = function () { + return this.socket.options.secure ? 'wss' : 'ws'; + }; + + /** + * Checks if the browser has support for native `WebSockets` and that + * it's not the polyfill created for the FlashSocket transport. + * + * @return {Boolean} + * @api public + */ + + WS.check = function () { + return ('WebSocket' in global && !('__addTask' in WebSocket)) + || 'MozWebSocket' in global; + }; + + /** + * Check if the `WebSocket` transport support cross domain communications. + * + * @returns {Boolean} + * @api public + */ + + WS.xdomainCheck = function () { + return true; + }; + + /** + * Add the transport to your public io.transports array. + * + * @api private + */ + + io.transports.push('websocket'); + +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports + , this +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.flashsocket = Flashsocket; + + /** + * The FlashSocket transport. This is a API wrapper for the HTML5 WebSocket + * specification. It uses a .swf file to communicate with the server. If you want + * to serve the .swf file from a other server than where the Socket.IO script is + * coming from you need to use the insecure version of the .swf. More information + * about this can be found on the github page. + * + * @constructor + * @extends {io.Transport.websocket} + * @api public + */ + + function Flashsocket () { + io.Transport.websocket.apply(this, arguments); + }; + + /** + * Inherits from Transport. + */ + + io.util.inherit(Flashsocket, io.Transport.websocket); + + /** + * Transport name + * + * @api public + */ + + Flashsocket.prototype.name = 'flashsocket'; + + /** + * Disconnect the established `FlashSocket` connection. This is done by adding a + * new task to the FlashSocket. The rest will be handled off by the `WebSocket` + * transport. + * + * @returns {Transport} + * @api public + */ + + Flashsocket.prototype.open = function () { + var self = this + , args = arguments; + + WebSocket.__addTask(function () { + io.Transport.websocket.prototype.open.apply(self, args); + }); + return this; + }; + + /** + * Sends a message to the Socket.IO server. This is done by adding a new + * task to the FlashSocket. The rest will be handled off by the `WebSocket` + * transport. + * + * @returns {Transport} + * @api public + */ + + Flashsocket.prototype.send = function () { + var self = this, args = arguments; + WebSocket.__addTask(function () { + io.Transport.websocket.prototype.send.apply(self, args); + }); + return this; + }; + + /** + * Disconnects the established `FlashSocket` connection. + * + * @returns {Transport} + * @api public + */ + + Flashsocket.prototype.close = function () { + WebSocket.__tasks.length = 0; + io.Transport.websocket.prototype.close.call(this); + return this; + }; + + /** + * The WebSocket fall back needs to append the flash container to the body + * element, so we need to make sure we have access to it. Or defer the call + * until we are sure there is a body element. + * + * @param {Socket} socket The socket instance that needs a transport + * @param {Function} fn The callback + * @api private + */ + + Flashsocket.prototype.ready = function (socket, fn) { + function init () { + var options = socket.options + , port = options['flash policy port'] + , path = [ + 'http' + (options.secure ? 's' : '') + ':/' + , options.host + ':' + options.port + , options.resource + , 'static/flashsocket' + , 'WebSocketMain' + (socket.isXDomain() ? 'Insecure' : '') + '.swf' + ]; + + // Only start downloading the swf file when the checked that this browser + // actually supports it + if (!Flashsocket.loaded) { + if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') { + // Set the correct file based on the XDomain settings + WEB_SOCKET_SWF_LOCATION = path.join('/'); + } + + if (port !== 843) { + WebSocket.loadFlashPolicyFile('xmlsocket://' + options.host + ':' + port); + } + + WebSocket.__initialize(); + Flashsocket.loaded = true; + } + + fn.call(self); + } + + var self = this; + if (document.body) return init(); + + io.util.load(init); + }; + + /** + * Check if the FlashSocket transport is supported as it requires that the Adobe + * Flash Player plug-in version `10.0.0` or greater is installed. And also check if + * the polyfill is correctly loaded. + * + * @returns {Boolean} + * @api public + */ + + Flashsocket.check = function () { + if ( + typeof WebSocket == 'undefined' + || !('__initialize' in WebSocket) || !swfobject + ) return false; + + return swfobject.getFlashPlayerVersion().major >= 10; + }; + + /** + * Check if the FlashSocket transport can be used as cross domain / cross origin + * transport. Because we can't see which type (secure or insecure) of .swf is used + * we will just return true. + * + * @returns {Boolean} + * @api public + */ + + Flashsocket.xdomainCheck = function () { + return true; + }; + + /** + * Disable AUTO_INITIALIZATION + */ + + if (typeof window != 'undefined') { + WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true; + } + + /** + * Add the transport to your public io.transports array. + * + * @api private + */ + + io.transports.push('flashsocket'); +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); +/* SWFObject v2.2 + is released under the MIT License +*/ +if ('undefined' != typeof window) { +var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O[(['Active'].concat('Object').join('X'))]!=D){try{var ad=new window[(['Active'].concat('Object').join('X'))](W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab +// License: New BSD License +// Reference: http://dev.w3.org/html5/websockets/ +// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol + +(function() { + + if ('undefined' == typeof window || window.WebSocket) return; + + var console = window.console; + if (!console || !console.log || !console.error) { + console = {log: function(){ }, error: function(){ }}; + } + + if (!swfobject.hasFlashPlayerVersion("10.0.0")) { + console.error("Flash Player >= 10.0.0 is required."); + return; + } + if (location.protocol == "file:") { + console.error( + "WARNING: web-socket-js doesn't work in file:///... URL " + + "unless you set Flash Security Settings properly. " + + "Open the page via Web server i.e. http://..."); + } + + /** + * This class represents a faux web socket. + * @param {string} url + * @param {array or string} protocols + * @param {string} proxyHost + * @param {int} proxyPort + * @param {string} headers + */ + WebSocket = function(url, protocols, proxyHost, proxyPort, headers) { + var self = this; + self.__id = WebSocket.__nextId++; + WebSocket.__instances[self.__id] = self; + self.readyState = WebSocket.CONNECTING; + self.bufferedAmount = 0; + self.__events = {}; + if (!protocols) { + protocols = []; + } else if (typeof protocols == "string") { + protocols = [protocols]; + } + // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc. + // Otherwise, when onopen fires immediately, onopen is called before it is set. + setTimeout(function() { + WebSocket.__addTask(function() { + WebSocket.__flash.create( + self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null); + }); + }, 0); + }; + + /** + * Send data to the web socket. + * @param {string} data The data to send to the socket. + * @return {boolean} True for success, false for failure. + */ + WebSocket.prototype.send = function(data) { + if (this.readyState == WebSocket.CONNECTING) { + throw "INVALID_STATE_ERR: Web Socket connection has not been established"; + } + // We use encodeURIComponent() here, because FABridge doesn't work if + // the argument includes some characters. We don't use escape() here + // because of this: + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions + // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't + // preserve all Unicode characters either e.g. "\uffff" in Firefox. + // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require + // additional testing. + var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data)); + if (result < 0) { // success + return true; + } else { + this.bufferedAmount += result; + return false; + } + }; + + /** + * Close this web socket gracefully. + */ + WebSocket.prototype.close = function() { + if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) { + return; + } + this.readyState = WebSocket.CLOSING; + WebSocket.__flash.close(this.__id); + }; + + /** + * Implementation of {@link DOM 2 EventTarget Interface} + * + * @param {string} type + * @param {function} listener + * @param {boolean} useCapture + * @return void + */ + WebSocket.prototype.addEventListener = function(type, listener, useCapture) { + if (!(type in this.__events)) { + this.__events[type] = []; + } + this.__events[type].push(listener); + }; + + /** + * Implementation of {@link DOM 2 EventTarget Interface} + * + * @param {string} type + * @param {function} listener + * @param {boolean} useCapture + * @return void + */ + WebSocket.prototype.removeEventListener = function(type, listener, useCapture) { + if (!(type in this.__events)) return; + var events = this.__events[type]; + for (var i = events.length - 1; i >= 0; --i) { + if (events[i] === listener) { + events.splice(i, 1); + break; + } + } + }; + + /** + * Implementation of {@link DOM 2 EventTarget Interface} + * + * @param {Event} event + * @return void + */ + WebSocket.prototype.dispatchEvent = function(event) { + var events = this.__events[event.type] || []; + for (var i = 0; i < events.length; ++i) { + events[i](event); + } + var handler = this["on" + event.type]; + if (handler) handler(event); + }; + + /** + * Handles an event from Flash. + * @param {Object} flashEvent + */ + WebSocket.prototype.__handleEvent = function(flashEvent) { + if ("readyState" in flashEvent) { + this.readyState = flashEvent.readyState; + } + if ("protocol" in flashEvent) { + this.protocol = flashEvent.protocol; + } + + var jsEvent; + if (flashEvent.type == "open" || flashEvent.type == "error") { + jsEvent = this.__createSimpleEvent(flashEvent.type); + } else if (flashEvent.type == "close") { + // TODO implement jsEvent.wasClean + jsEvent = this.__createSimpleEvent("close"); + } else if (flashEvent.type == "message") { + var data = decodeURIComponent(flashEvent.message); + jsEvent = this.__createMessageEvent("message", data); + } else { + throw "unknown event type: " + flashEvent.type; + } + + this.dispatchEvent(jsEvent); + }; + + WebSocket.prototype.__createSimpleEvent = function(type) { + if (document.createEvent && window.Event) { + var event = document.createEvent("Event"); + event.initEvent(type, false, false); + return event; + } else { + return {type: type, bubbles: false, cancelable: false}; + } + }; + + WebSocket.prototype.__createMessageEvent = function(type, data) { + if (document.createEvent && window.MessageEvent && !window.opera) { + var event = document.createEvent("MessageEvent"); + event.initMessageEvent("message", false, false, data, null, null, window, null); + return event; + } else { + // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes. + return {type: type, data: data, bubbles: false, cancelable: false}; + } + }; + + /** + * Define the WebSocket readyState enumeration. + */ + WebSocket.CONNECTING = 0; + WebSocket.OPEN = 1; + WebSocket.CLOSING = 2; + WebSocket.CLOSED = 3; + + WebSocket.__flash = null; + WebSocket.__instances = {}; + WebSocket.__tasks = []; + WebSocket.__nextId = 0; + + /** + * Load a new flash security policy file. + * @param {string} url + */ + WebSocket.loadFlashPolicyFile = function(url){ + WebSocket.__addTask(function() { + WebSocket.__flash.loadManualPolicyFile(url); + }); + }; + + /** + * Loads WebSocketMain.swf and creates WebSocketMain object in Flash. + */ + WebSocket.__initialize = function() { + if (WebSocket.__flash) return; + + if (WebSocket.__swfLocation) { + // For backword compatibility. + window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; + } + if (!window.WEB_SOCKET_SWF_LOCATION) { + console.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf"); + return; + } + var container = document.createElement("div"); + container.id = "webSocketContainer"; + // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents + // Flash from loading at least in IE. So we move it out of the screen at (-100, -100). + // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash + // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is + // the best we can do as far as we know now. + container.style.position = "absolute"; + if (WebSocket.__isFlashLite()) { + container.style.left = "0px"; + container.style.top = "0px"; + } else { + container.style.left = "-100px"; + container.style.top = "-100px"; + } + var holder = document.createElement("div"); + holder.id = "webSocketFlash"; + container.appendChild(holder); + document.body.appendChild(container); + // See this article for hasPriority: + // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html + swfobject.embedSWF( + WEB_SOCKET_SWF_LOCATION, + "webSocketFlash", + "1" /* width */, + "1" /* height */, + "10.0.0" /* SWF version */, + null, + null, + {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"}, + null, + function(e) { + if (!e.success) { + console.error("[WebSocket] swfobject.embedSWF failed"); + } + }); + }; + + /** + * Called by Flash to notify JS that it's fully loaded and ready + * for communication. + */ + WebSocket.__onFlashInitialized = function() { + // We need to set a timeout here to avoid round-trip calls + // to flash during the initialization process. + setTimeout(function() { + WebSocket.__flash = document.getElementById("webSocketFlash"); + WebSocket.__flash.setCallerUrl(location.href); + WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG); + for (var i = 0; i < WebSocket.__tasks.length; ++i) { + WebSocket.__tasks[i](); + } + WebSocket.__tasks = []; + }, 0); + }; + + /** + * Called by Flash to notify WebSockets events are fired. + */ + WebSocket.__onFlashEvent = function() { + setTimeout(function() { + try { + // Gets events using receiveEvents() instead of getting it from event object + // of Flash event. This is to make sure to keep message order. + // It seems sometimes Flash events don't arrive in the same order as they are sent. + var events = WebSocket.__flash.receiveEvents(); + for (var i = 0; i < events.length; ++i) { + WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]); + } + } catch (e) { + console.error(e); + } + }, 0); + return true; + }; + + // Called by Flash. + WebSocket.__log = function(message) { + console.log(decodeURIComponent(message)); + }; + + // Called by Flash. + WebSocket.__error = function(message) { + console.error(decodeURIComponent(message)); + }; + + WebSocket.__addTask = function(task) { + if (WebSocket.__flash) { + task(); + } else { + WebSocket.__tasks.push(task); + } + }; + + /** + * Test if the browser is running flash lite. + * @return {boolean} True if flash lite is running, false otherwise. + */ + WebSocket.__isFlashLite = function() { + if (!window.navigator || !window.navigator.mimeTypes) { + return false; + } + var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"]; + if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) { + return false; + } + return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; + }; + + if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { + if (window.addEventListener) { + window.addEventListener("load", function(){ + WebSocket.__initialize(); + }, false); + } else { + window.attachEvent("onload", function(){ + WebSocket.__initialize(); + }); + } + } + +})(); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + + /** + * Expose constructor. + * + * @api public + */ + + exports.XHR = XHR; + + /** + * XHR constructor + * + * @costructor + * @api public + */ + + function XHR (socket) { + if (!socket) return; + + io.Transport.apply(this, arguments); + this.sendBuffer = []; + }; + + /** + * Inherits from Transport. + */ + + io.util.inherit(XHR, io.Transport); + + /** + * Establish a connection + * + * @returns {Transport} + * @api public + */ + + XHR.prototype.open = function () { + this.socket.setBuffer(false); + this.onOpen(); + this.get(); + + // we need to make sure the request succeeds since we have no indication + // whether the request opened or not until it succeeded. + this.setCloseTimeout(); + + return this; + }; + + /** + * Check if we need to send data to the Socket.IO server, if we have data in our + * buffer we encode it and forward it to the `post` method. + * + * @api private + */ + + XHR.prototype.payload = function (payload) { + var msgs = []; + + for (var i = 0, l = payload.length; i < l; i++) { + msgs.push(io.parser.encodePacket(payload[i])); + } + + this.send(io.parser.encodePayload(msgs)); + }; + + /** + * Send data to the Socket.IO server. + * + * @param data The message + * @returns {Transport} + * @api public + */ + + XHR.prototype.send = function (data) { + this.post(data); + return this; + }; + + /** + * Posts a encoded message to the Socket.IO server. + * + * @param {String} data A encoded message. + * @api private + */ + + function empty () { }; + + XHR.prototype.post = function (data) { + var self = this; + this.socket.setBuffer(true); + + function stateChange () { + if (this.readyState == 4) { + this.onreadystatechange = empty; + self.posting = false; + + if (this.status == 200){ + self.socket.setBuffer(false); + } else { + self.onClose(); + } + } + } + + function onload () { + this.onload = empty; + self.socket.setBuffer(false); + }; + + this.sendXHR = this.request('POST'); + + if (global.XDomainRequest && this.sendXHR instanceof XDomainRequest) { + this.sendXHR.onload = this.sendXHR.onerror = onload; + } else { + this.sendXHR.onreadystatechange = stateChange; + } + + this.sendXHR.send(data); + }; + + /** + * Disconnects the established `XHR` connection. + * + * @returns {Transport} + * @api public + */ + + XHR.prototype.close = function () { + this.onClose(); + return this; + }; + + /** + * Generates a configured XHR request + * + * @param {String} url The url that needs to be requested. + * @param {String} method The method the request should use. + * @returns {XMLHttpRequest} + * @api private + */ + + XHR.prototype.request = function (method) { + var req = io.util.request(this.socket.isXDomain()) + , query = io.util.query(this.socket.options.query, 't=' + +new Date); + + req.open(method || 'GET', this.prepareUrl() + query, true); + + if (method == 'POST') { + try { + if (req.setRequestHeader) { + req.setRequestHeader('Content-type', 'text/plain;charset=UTF-8'); + } else { + // XDomainRequest + req.contentType = 'text/plain'; + } + } catch (e) {} + } + + return req; + }; + + /** + * Returns the scheme to use for the transport URLs. + * + * @api private + */ + + XHR.prototype.scheme = function () { + return this.socket.options.secure ? 'https' : 'http'; + }; + + /** + * Check if the XHR transports are supported + * + * @param {Boolean} xdomain Check if we support cross domain requests. + * @returns {Boolean} + * @api public + */ + + XHR.check = function (socket, xdomain) { + try { + var request = io.util.request(xdomain), + usesXDomReq = (global.XDomainRequest && request instanceof XDomainRequest), + socketProtocol = (socket && socket.options && socket.options.secure ? 'https:' : 'http:'), + isXProtocol = (socketProtocol != global.location.protocol); + if (request && !(usesXDomReq && isXProtocol)) { + return true; + } + } catch(e) {} + + return false; + }; + + /** + * Check if the XHR transport supports cross domain requests. + * + * @returns {Boolean} + * @api public + */ + + XHR.xdomainCheck = function (socket) { + return XHR.check(socket, true); + }; + +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports + , this +); +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io) { + + /** + * Expose constructor. + */ + + exports.htmlfile = HTMLFile; + + /** + * The HTMLFile transport creates a `forever iframe` based transport + * for Internet Explorer. Regular forever iframe implementations will + * continuously trigger the browsers buzy indicators. If the forever iframe + * is created inside a `htmlfile` these indicators will not be trigged. + * + * @constructor + * @extends {io.Transport.XHR} + * @api public + */ + + function HTMLFile (socket) { + io.Transport.XHR.apply(this, arguments); + }; + + /** + * Inherits from XHR transport. + */ + + io.util.inherit(HTMLFile, io.Transport.XHR); + + /** + * Transport name + * + * @api public + */ + + HTMLFile.prototype.name = 'htmlfile'; + + /** + * Creates a new Ac...eX `htmlfile` with a forever loading iframe + * that can be used to listen to messages. Inside the generated + * `htmlfile` a reference will be made to the HTMLFile transport. + * + * @api private + */ + + HTMLFile.prototype.get = function () { + this.doc = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); + this.doc.open(); + this.doc.write(''); + this.doc.close(); + this.doc.parentWindow.s = this; + + var iframeC = this.doc.createElement('div'); + iframeC.className = 'socketio'; + + this.doc.body.appendChild(iframeC); + this.iframe = this.doc.createElement('iframe'); + + iframeC.appendChild(this.iframe); + + var self = this + , query = io.util.query(this.socket.options.query, 't='+ +new Date); + + this.iframe.src = this.prepareUrl() + query; + + io.util.on(window, 'unload', function () { + self.destroy(); + }); + }; + + /** + * The Socket.IO server will write script tags inside the forever + * iframe, this function will be used as callback for the incoming + * information. + * + * @param {String} data The message + * @param {document} doc Reference to the context + * @api private + */ + + HTMLFile.prototype._ = function (data, doc) { + this.onData(data); + try { + var script = doc.getElementsByTagName('script')[0]; + script.parentNode.removeChild(script); + } catch (e) { } + }; + + /** + * Destroy the established connection, iframe and `htmlfile`. + * And calls the `CollectGarbage` function of Internet Explorer + * to release the memory. + * + * @api private + */ + + HTMLFile.prototype.destroy = function () { + if (this.iframe){ + try { + this.iframe.src = 'about:blank'; + } catch(e){} + + this.doc = null; + this.iframe.parentNode.removeChild(this.iframe); + this.iframe = null; + + CollectGarbage(); + } + }; + + /** + * Disconnects the established connection. + * + * @returns {Transport} Chaining. + * @api public + */ + + HTMLFile.prototype.close = function () { + this.destroy(); + return io.Transport.XHR.prototype.close.call(this); + }; + + /** + * Checks if the browser supports this transport. The browser + * must have an `Ac...eXObject` implementation. + * + * @return {Boolean} + * @api public + */ + + HTMLFile.check = function (socket) { + if (typeof window != "undefined" && (['Active'].concat('Object').join('X')) in window){ + try { + var a = new window[(['Active'].concat('Object').join('X'))]('htmlfile'); + return a && io.Transport.XHR.check(socket); + } catch(e){} + } + return false; + }; + + /** + * Check if cross domain requests are supported. + * + * @returns {Boolean} + * @api public + */ + + HTMLFile.xdomainCheck = function () { + // we can probably do handling for sub-domains, we should + // test that it's cross domain but a subdomain here + return false; + }; + + /** + * Add the transport to your public io.transports array. + * + * @api private + */ + + io.transports.push('htmlfile'); + +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + + /** + * Expose constructor. + */ + + exports['xhr-polling'] = XHRPolling; + + /** + * The XHR-polling transport uses long polling XHR requests to create a + * "persistent" connection with the server. + * + * @constructor + * @api public + */ + + function XHRPolling () { + io.Transport.XHR.apply(this, arguments); + }; + + /** + * Inherits from XHR transport. + */ + + io.util.inherit(XHRPolling, io.Transport.XHR); + + /** + * Merge the properties from XHR transport + */ + + io.util.merge(XHRPolling, io.Transport.XHR); + + /** + * Transport name + * + * @api public + */ + + XHRPolling.prototype.name = 'xhr-polling'; + + /** + * Indicates whether heartbeats is enabled for this transport + * + * @api private + */ + + XHRPolling.prototype.heartbeats = function () { + return false; + }; + + /** + * Establish a connection, for iPhone and Android this will be done once the page + * is loaded. + * + * @returns {Transport} Chaining. + * @api public + */ + + XHRPolling.prototype.open = function () { + var self = this; + + io.Transport.XHR.prototype.open.call(self); + return false; + }; + + /** + * Starts a XHR request to wait for incoming messages. + * + * @api private + */ + + function empty () {}; + + XHRPolling.prototype.get = function () { + if (!this.isOpen) return; + + var self = this; + + function stateChange () { + if (this.readyState == 4) { + this.onreadystatechange = empty; + + if (this.status == 200) { + self.onData(this.responseText); + self.get(); + } else { + self.onClose(); + } + } + }; + + function onload () { + this.onload = empty; + this.onerror = empty; + self.onData(this.responseText); + self.get(); + }; + + function onerror () { + self.onClose(); + }; + + this.xhr = this.request(); + + if (global.XDomainRequest && this.xhr instanceof XDomainRequest) { + this.xhr.onload = onload; + this.xhr.onerror = onerror; + } else { + this.xhr.onreadystatechange = stateChange; + } + + this.xhr.send(null); + }; + + /** + * Handle the unclean close behavior. + * + * @api private + */ + + XHRPolling.prototype.onClose = function () { + io.Transport.XHR.prototype.onClose.call(this); + + if (this.xhr) { + this.xhr.onreadystatechange = this.xhr.onload = this.xhr.onerror = empty; + try { + this.xhr.abort(); + } catch(e){} + this.xhr = null; + } + }; + + /** + * Webkit based browsers show a infinit spinner when you start a XHR request + * before the browsers onload event is called so we need to defer opening of + * the transport until the onload event is called. Wrapping the cb in our + * defer method solve this. + * + * @param {Socket} socket The socket instance that needs a transport + * @param {Function} fn The callback + * @api private + */ + + XHRPolling.prototype.ready = function (socket, fn) { + var self = this; + + io.util.defer(function () { + fn.call(self); + }); + }; + + /** + * Add the transport to your public io.transports array. + * + * @api private + */ + + io.transports.push('xhr-polling'); + +})( + 'undefined' != typeof io ? io.Transport : module.exports + , 'undefined' != typeof io ? io : module.parent.exports + , this +); + +/** + * socket.io + * Copyright(c) 2011 LearnBoost + * MIT Licensed + */ + +(function (exports, io, global) { + /** + * There is a way to hide the loading indicator in Firefox. If you create and + * remove a iframe it will stop showing the current loading indicator. + * Unfortunately we can't feature detect that and UA sniffing is evil. + * + * @api private + */ + + var indicator = global.document && "MozAppearance" in + global.document.documentElement.style; + + /** + * Expose constructor. + */ + + exports['jsonp-polling'] = JSONPPolling; + + /** + * The JSONP transport creates an persistent connection by dynamically + * inserting a script tag in the page. This script tag will receive the + * information of the Socket.IO server. When new information is received + * it creates a new script tag for the new data stream. + * + * @constructor + * @extends {io.Transport.xhr-polling} + * @api public + */ + + function JSONPPolling (socket) { + io.Transport['xhr-polling'].apply(this, arguments); + + this.index = io.j.length; + + var self = this; + + io.j.push(function (msg) { + self._(msg); + }); + }; + + /** + * Inherits from XHR polling transport. + */ + + io.util.inherit(JSONPPolling, io.Transport['xhr-polling']); + + /** + * Transport name + * + * @api public + */ + + JSONPPolling.prototype.name = 'jsonp-polling'; + + /** + * Posts a encoded message to the Socket.IO server using an iframe. + * The iframe is used because script tags can create POST based requests. + * The iframe is positioned outside of the view so the user does not + * notice it's existence. + * + * @param {String} data A encoded message. + * @api private + */ + + JSONPPolling.prototype.post = function (data) { + var self = this + , query = io.util.query( + this.socket.options.query + , 't='+ (+new Date) + '&i=' + this.index + ); + + if (!this.form) { + var form = document.createElement('form') + , area = document.createElement('textarea') + , id = this.iframeId = 'socketio_iframe_' + this.index + , iframe; + + form.className = 'socketio'; + form.style.position = 'absolute'; + form.style.top = '0px'; + form.style.left = '0px'; + form.style.display = 'none'; + form.target = id; + form.method = 'POST'; + form.setAttribute('accept-charset', 'utf-8'); + area.name = 'd'; + form.appendChild(area); + document.body.appendChild(form); + + this.form = form; + this.area = area; + } + + this.form.action = this.prepareUrl() + query; + + function complete () { + initIframe(); + self.socket.setBuffer(false); + }; + + function initIframe () { + if (self.iframe) { + self.form.removeChild(self.iframe); + } + + try { + // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) + iframe = document.createElement('