{"id":13,"date":"2025-09-30T16:43:22","date_gmt":"2025-09-30T16:43:22","guid":{"rendered":"https:\/\/sites.clarkson.edu\/arboretum\/?page_id=13"},"modified":"2025-12-02T14:25:18","modified_gmt":"2025-12-02T14:25:18","slug":"species","status":"publish","type":"page","link":"https:\/\/sites.clarkson.edu\/arboretum\/species\/","title":{"rendered":"Species"},"content":{"rendered":"\n<body>\n    <link rel=\"stylesheet\" href=\"https:\/\/fonts.googleapis.com\/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0&#038;icon_names=keyboard_arrow_down\" \/>\n    \n    <div id=\"species-container\">Loading&#8230;<\/div>\n    <script>\n        \/\/ Function to retrieve and process species from ArcGIS Layer\n        async function getSpecies() {\n            const baseURL = \"https:\/\/services2.arcgis.com\/LNyGjxxQBXWW0vo0\/arcgis\/rest\/services\/Trees_6_view\/FeatureServer\/0\/query\";\n            const params = {\n                where:\"heritage='Yes'\",\n                fullText:\"\",\n                objectIds:\"\",\n                geometry:\"\",\n                geometryType:\"esriGeometryEnvelope\",\n                inSR:\"\",\n                spatialRel:\"esriSpatialRelIntersects\",\n                resultType:\"none\",\n                distance:0.0,\n                units:\"esriSRUnit_Meter\",\n                outDistance:\"\",\n                relationParam:\"\",\n                returnGeodetic:false,\n                outFields:\"assetid, commonname, genus, species\",\n                returnGeometry:false,\n                featureEncoding:\"esriDefault\",\n                multipatchOption:\"xyFootprint\",\n                maxAllowableOffset:\"\",\n                geometryPrecision:\"\",\n                outSR:\"\",\n                defaultSR:\"\",\n                datumTransformation:\"\",\n                applyVCSProjection:false,\n                returnIdsOnly:false,\n                returnUniqueIdsOnly:false,\n                returnCountOnly:false,\n                returnExtentOnly:false,\n                returnQueryGeometry:false,\n                returnDistinctValues:false,\n                cacheHint:false,\n                collation:\"\",\n                orderByFields:\"\",\n                groupByFieldsForStatistics:\"\",\n                returnAggIds:false,\n                outStatistics:\"\",\n                having:\"\",\n                resultOffset:\"\",\n                resultRecordCount:\"\",\n                returnZ:false,\n                returnM:false,\n                returnTrueCurves:false,\n                returnExceededLimitFeatures:true,\n                quantizationParameters:\"\",\n                sqlFormat:\"none\",\n                f:\"json\",\n                token:\"\"\n            };\n            let data = await fetch(baseURL+\"?\"+(new URLSearchParams(params)).toString()).then(res=>res.json()) \/\/ Get data and convert to json\n            \/\/ Create final species list, and working list to handle bad data\n            let species = []\n            let unsortedSpecies0 = []\n\n            \/\/ Loop through each species, and add data with full scientific names to species list (grouping by type)\n            data.features.forEach(item=>{\n\n                \/\/ Get wanted data for current tree\n                let treeData = {\n                    ids:[item.attributes.assetid],\n                    commonname: item.attributes.commonname ? item.attributes.commonname : \"\",\n                    genus: item.attributes.genus ? item.attributes.genus : \"\",\n                    species: item.attributes.species ? item.attributes.species : \"\"\n                }\n\n                \/\/ If the tree has a valid genus and species, sort it into list, otherwise add to unsorted list and continue\n                if (treeData.genus && treeData.species) {\n                    \/\/ Attempt to match current tree with species in list\n                    let speciesEntryIndex = species.findIndex(item=>item.genus==treeData.genus && item.species==treeData.species)\n                    if (speciesEntryIndex==-1) { \/\/ If no match, create new species entry\n                        species.push(treeData)\n                    } else { \/\/ If match, add tree type to species list\n                        species[speciesEntryIndex].ids.push(treeData.ids[0]); \n                    }\n                } else { \/\/ Does not have full scientific name\n                    unsortedSpecies0.push(treeData)\n                }\n            })\n\n            \/\/ If the tree has the same common name as species in list (Besides \"Other\"), sort it in\n            let unsortedSpecies1 = []\n            unsortedSpecies0.forEach(treeData=>{\n                \/\/ Ensure tree has common name, and is not \"Other\"\n                if (treeData.commonname && treeData.commonname != \"Other\") {\n                    \/\/ Attempt to match by common name\n                    let speciesEntryIndex = species.findIndex(item=>item.commonname==treeData.commonname)\n                    if (speciesEntryIndex>=0) { \/\/ If there is a match, add data to species list\n                        species[speciesEntryIndex].ids.push(treeData.ids[0])\n                    } else { \/\/ If no match, add data to new unsorted list\n                        unsortedSpecies1.push(treeData)\n                    }\n                } else { \/\/ If common name is \"Other\" (or null), push to next sort\n                    unsortedSpecies1.push(treeData)\n                }\n            })\n\n            \/\/ If the tree has the same genus as a species in list, sort it in, otherwise create new entry\n            let unsortedSpecies2 = [];\n            unsortedSpecies1.forEach(treeData=> {\n                \/\/ Ensure tree has genus    \n                if (treeData.genus) {\n                    \/\/ Attempt to match by genus\n                    let speciesEntryIndex = species.findIndex(item=>item.genus==treeData.genus)\n                    if (speciesEntryIndex>=0) { \/\/ If there is a match, add data to species list\n                        species[speciesEntryIndex].ids.push(treeData.ids[0])\n                    } else { \/\/ If there is no match, add to species list as genus alone\n                        species.push(treeData)\n                    }\n                } else { \/\/ If no genus, add to final unsorted list\n                    unsortedSpecies2.push(treeData)\n                }\n            })\n\n            \/\/ Attempt to match by common name for 2nd time, if match, sort in, if no match, create new entry\n            unsortedSpecies2.forEach(treeData=>{\n                let speciesEntryIndex = species.findIndex(item=>item.commonname==treeData.commonname);\n                if (speciesEntryIndex>=0) { \/\/ If match, add to species list entry\n                    species[speciesEntryIndex].ids.push(treeData.ids[0])\n                } else { \/\/ If no match, create new entry with present data\n                    species.psuh(treeData)\n                }\n            })\n\n            \/\/ Sort alphabetically\n            species.sort((a,b)=>a.species.localeCompare(b.species))\n            species.sort((a,b)=>a.genus.localeCompare(b.genus))\n            species.sort((a,b)=>a.commonname.localeCompare(b.commonname))\n\n            return species\n        }\n\n        async function getTrees() {\n            const baseURL = \"https:\/\/services2.arcgis.com\/LNyGjxxQBXWW0vo0\/arcgis\/rest\/services\/Trees_6_view\/FeatureServer\/0\/query\";\n            const params = {\n                where:\"heritage='Yes'\",\n                fullText:\"\",\n                objectIds:\"\",\n                geometry:\"\",\n                geometryType:\"esriGeometryEnvelope\",\n                inSR:\"\",\n                spatialRel:\"esriSpatialRelIntersects\",\n                resultType:\"none\",\n                distance:0.0,\n                units:\"esriSRUnit_Meter\",\n                outDistance:\"\",\n                relationParam:\"\",\n                returnGeodetic:false,\n                outFields:\"assetid, OBJECTID\",\n                returnGeometry:true,\n                featureEncoding:\"esriDefault\",\n                multipatchOption:\"xyFootprint\",\n                maxAllowableOffset:\"\",\n                geometryPrecision:\"\",\n                outSR:\"\",\n                defaultSR:\"\",\n                datumTransformation:\"\",\n                applyVCSProjection:false,\n                returnIdsOnly:false,\n                returnUniqueIdsOnly:false,\n                returnCountOnly:false,\n                returnExtentOnly:false,\n                returnQueryGeometry:false,\n                returnDistinctValues:false,\n                cacheHint:false,\n                collation:\"\",\n                orderByFields:\"\",\n                groupByFieldsForStatistics:\"\",\n                returnAggIds:false,\n                outStatistics:\"\",\n                having:\"\",\n                resultOffset:\"\",\n                resultRecordCount:\"\",\n                returnZ:false,\n                returnM:false,\n                returnTrueCurves:false,\n                returnExceededLimitFeatures:true,\n                quantizationParameters:\"\",\n                sqlFormat:\"none\",\n                f:\"json\",\n                token:\"\"\n            };\n            let data = await fetch(baseURL+\"?\"+(new URLSearchParams(params)).toString()).then(res=>res.json()) \/\/ Get data and convert to json\n            return data.features\n        }\n\n        \/\/ Function to update the visual listing of the \n        async function updateSpeciesList() {\n            \/\/ Fetch data and store HTML Elements\n            let species = await getSpecies();\n            let trees = await getTrees();\n            console.log(trees)\n            let speciesContainer = document.getElementById('species-container');\n\n            \/\/ Clear output\n            speciesContainer.innerHTML='';\n\n            \/\/ Add entry for each species\n            species.forEach(item=>{\n                \/\/ Create box for the species\n                let speciesBox = document.createElement('div');\n                speciesBox.classList.add('species-box')\n\n                let speciesHeading = document.createElement('button');\n                speciesHeading.classList.add('species-heading');\n\n                let speciesHeadingText = document.createElement('h3');\n                if (item.commonname == \"Other\")\n                    speciesHeadingText.innerText = `${item.species} ${item.genus}`\n                else if (item.species == \"\" && \"item.genus\" == \"\")\n                    speciesHeadingText = item.commonname\n                else\n                    speciesHeadingText.innerText = `${item.commonname}: ${item.species} ${item.genus}`;\n                speciesHeading.appendChild(speciesHeadingText);\n                \n                let showMoreIcon = document.createElement('span');\n                showMoreIcon.classList.add('show-more-icon', 'material-symbols-outlined');\n                showMoreIcon.innerHTML = 'keyboard_arrow_down'\n                speciesHeading.appendChild(showMoreIcon)\n                \n                speciesBox.appendChild(speciesHeading);\n\n\n                let speciesContent = document.createElement('div');\n                speciesContent.classList.add('species-content')\n\n                let treeListHeading = document.createElement('h4');\n                treeListHeading.innerText = \"Find Trees:\"\n                speciesContent.appendChild(treeListHeading);\n\n                let treeList = document.createElement('ul');\n                item.ids.forEach(treeID=>{\n                    let treeItem = document.createElement('li');\n                    let treeLink = document.createElement('a');\n                    treeLink.textContent = treeID;\n\n                    let tree = trees.find(item=>item.attributes.assetid==treeID)\n                    console.log(tree)\n                    let treeLocation = tree.geometry\n                    treeLocation = webMercatorToLatLon(treeLocation);\n                    treeLink.href = experienceLink(tree.attributes.OBJECTID);\n                    treeLink.target = \"_blank\";\n\n                    treeItem.appendChild(treeLink);\n                    treeList.appendChild(treeItem);\n                })\n                speciesContent.appendChild(treeList);\n\n                speciesBox.appendChild(speciesContent)\n\n                speciesContainer.appendChild(speciesBox);\n\n                speciesHeading.addEventListener('click', ()=>{\n                    speciesBox.classList.toggle('open');\n                })\n\n            })\n        }\n\n        try {\n            updateSpeciesList()\n        } catch (error) {\n            console.log(error)\n            document.getElementById('species-container').innerHTML=\"Failed to load\"\n        }\n\n        \/\/ Function to convert between web mercator and lat\/lon coords\n        \/\/ AI generated using ChatGPT\n        function webMercatorToLatLon({x, y}) {\n            const R = 6378137.0; \/\/ Earth\u2019s radius in meters\n            const lon = (x \/ R) * (180 \/ Math.PI);\n            const lat = (2 * Math.atan(Math.exp(y \/ R)) - Math.PI \/ 2) * (180 \/ Math.PI);\n            return { lat, lon };\n        }\n\n        \/\/ Function to create Google Maps link with given lat\/lon\n        function mapsLink({lat, lon}) {\n            let mapsURL = \"https:\/\/www.google.com\/maps\/place\/\"+lat+\",\"+lon\n            return mapsURL\n        }\n\n        \/\/ Function to create ArcGIS experience link to given feature\n        function experienceLink(OBJECTID) {\n            let URL = 'https:\/\/experience.arcgis.com\/experience\/b3a008a0fd88470184e97f1ada96593d\/#data_s=id%3A004749f601664646b43c61366aabfab8-19980ac2e2b-layer-4%3A' + OBJECTID\n            return URL;\n        }\n    <\/script>\n<\/body>\n<style class=\"advgb-styles-renderer\">\n        .species-box {\n            border: 1px solid black;\n            padding: 0.5rem 1rem 0.5rem 1rem;\n            margin-bottom: 1rem;\n        }\n        \n        .species-heading {\n            outline: none;\n            border: none;\n            background: none;\n            display: flex;\n            align-items: center;\n            justify-content: space-between;\n            width: 100%;\n            cursor: pointer;\n        }\n\n        .species-content {\n            interpolate-size: allow-keywords;\n            height: 0;\n            overflow: hidden;\n            transition: height 400ms ease-in-out;\n        }\n\n        .species-box.open > .species-content {\n            height: auto;\n        }\n\n        .show-more-icon {\n            transition: rotate 400ms ease-in-out;\n        }\n\n        .species-box.open .show-more-icon {\n            rotate: 180deg;\n        }\n    <\/style>","protected":false},"excerpt":{"rendered":"<p>Loading&#8230;<\/p>\n","protected":false},"author":244,"featured_media":376,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","footnotes":""},"class_list":["post-13","page","type-page","status-publish","has-post-thumbnail","hentry"],"coauthors":[],"author_meta":{"author_link":"https:\/\/sites.clarkson.edu\/arboretum\/author\/bartolsm\/","display_name":"bartolsm"},"relative_dates":{"created":"Posted 10 months ago","modified":"Updated 8 months ago"},"absolute_dates":{"created":"Posted on September 30, 2025","modified":"Updated on December 2, 2025"},"absolute_dates_time":{"created":"Posted on September 30, 2025 4:43 pm","modified":"Updated on December 2, 2025 2:25 pm"},"featured_img_caption":"","featured_img":"https:\/\/sites.clarkson.edu\/arboretum\/wp-content\/uploads\/sites\/104\/2025\/11\/island7-min-scaled.jpg","series_order":"","_links":{"self":[{"href":"https:\/\/sites.clarkson.edu\/arboretum\/wp-json\/wp\/v2\/pages\/13","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sites.clarkson.edu\/arboretum\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/sites.clarkson.edu\/arboretum\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/sites.clarkson.edu\/arboretum\/wp-json\/wp\/v2\/users\/244"}],"replies":[{"embeddable":true,"href":"https:\/\/sites.clarkson.edu\/arboretum\/wp-json\/wp\/v2\/comments?post=13"}],"version-history":[{"count":9,"href":"https:\/\/sites.clarkson.edu\/arboretum\/wp-json\/wp\/v2\/pages\/13\/revisions"}],"predecessor-version":[{"id":462,"href":"https:\/\/sites.clarkson.edu\/arboretum\/wp-json\/wp\/v2\/pages\/13\/revisions\/462"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sites.clarkson.edu\/arboretum\/wp-json\/wp\/v2\/media\/376"}],"wp:attachment":[{"href":"https:\/\/sites.clarkson.edu\/arboretum\/wp-json\/wp\/v2\/media?parent=13"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}