├── .gitignore
├── CONTRIBUTING.md
├── Gruntfile.js
├── README.md
├── building-viewer.png
├── docs
├── NameConventions.md
├── Quickstart.md
├── Sections.md
└── images
│ ├── screenshot_1.png
│ ├── screenshot_2.png
│ ├── screenshot_3.png
│ ├── screenshot_4.png
│ ├── screenshot_5.png
│ ├── screenshot_6.png
│ ├── screenshot_7.png
│ └── screenshot_8.png
├── index.html
├── license.txt
├── package-lock.json
├── package.json
├── src
├── css
│ ├── main.scss
│ ├── mixins.scss
│ └── variables.scss
└── js
│ ├── AppState.ts
│ ├── BuildingViewer.tsx
│ ├── config.tsx
│ ├── sections
│ ├── FloorsSection.tsx
│ ├── HomeSection.tsx
│ ├── Section.ts
│ ├── Sections.tsx
│ ├── SurroundingsSection.tsx
│ └── css
│ │ └── sections.scss
│ ├── support
│ ├── BuildingVisualisation.ts
│ ├── SurroundingsVisualisation.ts
│ ├── appUtils.ts
│ ├── buildingSceneLayerUtils.ts
│ └── visualVariables.ts
│ └── widgets
│ ├── FloorSelector
│ ├── FloorSelector.tsx
│ └── css
│ │ └── floorSelector.scss
│ ├── Popup
│ ├── Popup.tsx
│ ├── PopupInfo.ts
│ └── css
│ │ └── popup.scss
│ ├── Timetable
│ ├── Timetable.tsx
│ └── css
│ │ └── timetable.scss
│ ├── Toggle
│ ├── Toggle.tsx
│ └── css
│ │ └── toggle.scss
│ ├── Viewpoints
│ ├── OneViewpoint.tsx
│ ├── Viewpoints.tsx
│ └── css
│ │ └── viewpoints.scss
│ └── widgets.scss
├── tsconfig.json
├── tslint.json
└── typings
└── arcgis-js-api-4.20.d.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 | src/**/*.js*
3 | src/**/*.css
4 | *.css.map
5 | css/**/*.css*
6 | .sass-cache/*
7 | dist/
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contributing to ArcGIS API for JavaScript
2 |
3 | This guide describes how you can contribute code improvements to the ArcGIS API for JavaScript.
4 |
5 | 1. Review the [README](README.md).
6 | 2. Create a new feature branch in your local repo.
7 | * The name of the branch doesn't matter, but as a best practice use a descriptive name like "add-annotation-layer".
8 | 3. Write code to add an enhancement or fix the problem.
9 | 4. Test your code.
10 |
11 | ### Submitting changes
12 |
13 | 1. Push your feature branch to the repo or your fork of the repo if you don't have push access.
14 | 2. Submit a [pull request](https://help.github.com/articles/using-pull-requests) against the "master" branch.
15 | * Clearly describe the issue including steps to reproduce; or if an enhancement, indicate the functionality you built.
16 |
17 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function(grunt) {
2 | var options = {
3 | livereload: true,
4 | port: grunt.option('port') || 8888,
5 | hostname: grunt.option('server') || "localhost",
6 | };
7 |
8 | // Project configuration.
9 | grunt.initConfig({
10 | watch: {
11 | livereload: {
12 | // Here we watch any files changed
13 | options: {
14 | livereload: true
15 | },
16 | files: [
17 | 'src/**/*.js',
18 | 'src/**/*.css',
19 | 'src/**/*.html'
20 | ]
21 | }
22 | },
23 | connect: {
24 | app: {
25 | options: options
26 | }
27 | },
28 | concat : {
29 | dist : {
30 | src : ['src/js/**/*.js'],
31 | dest : '.tmp/main.js'
32 | }
33 | },
34 | uglify : {
35 | dist : {
36 | src : 'dist/main.min.js',
37 | dest : 'dist/main.min.js'
38 | }
39 | },
40 | htmlmin: {
41 | dist: {
42 | options: {
43 | removeComments: true,
44 | collapseWhitespace: true
45 | },
46 | files: {
47 | 'dist/index.html': 'dist/index.html'
48 | }
49 | }
50 | },
51 | copy: {
52 | dist: {
53 | files: [
54 | {
55 | expand: true,
56 | src: ['src/**/*.css'],
57 | dest: 'dist/'
58 | }
59 | ]
60 | }
61 | },
62 | comments: {
63 | dist: {
64 | options: {
65 | singleline: true,
66 | multiline: true
67 | },
68 | src: [ 'dist/*.html' ]
69 | }
70 | },
71 | includeSource: {
72 | options: {
73 | basePath: 'dist'
74 | },
75 | dist: {
76 | files: {
77 | 'dist/index.html': './index.html'
78 | }
79 | }
80 | },
81 | run: {
82 | tscDist: {
83 | cmd: 'npx',
84 | args: [
85 | 'tsc',
86 | '--outFile',
87 | 'dist/main.min.js'
88 | ]
89 | }
90 | }
91 | });
92 |
93 | // dist
94 | grunt.loadNpmTasks('grunt-contrib-uglify');
95 | grunt.loadNpmTasks('grunt-contrib-htmlmin');
96 | grunt.loadNpmTasks('grunt-contrib-concat');
97 | grunt.loadNpmTasks('grunt-contrib-copy');
98 | grunt.loadNpmTasks('grunt-stripcomments');
99 | grunt.loadNpmTasks('grunt-include-source');
100 | grunt.loadNpmTasks('grunt-run');
101 |
102 |
103 | // Load grunt plugins
104 | grunt.loadNpmTasks('grunt-contrib-watch');
105 | grunt.loadNpmTasks('grunt-contrib-connect');
106 |
107 |
108 | // Register tasks
109 | grunt.registerTask("default", ["connect", "watch"]);
110 | grunt.registerTask("dist", ["run:tscDist", "includeSource:dist", "copy:dist", "uglify:dist", "htmlmin:dist", "comments:dist"]);
111 | };
112 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Building Viewer
2 |
3 | This demonstrates the use of [ArcGIS API 4 for JavaScript](https://developers.arcgis.com/javascript/) and [Building Scene Layers](https://developers.arcgis.com/javascript/latest/api-reference/) in a compelling website.
4 |
5 | The application presents the [Turanga library](https://my.christchurchcitylibraries.com/turanga/) in 3D. The visitor can explore the library by navigating around, and then inside, floor by floor, to discover this amazing building.
6 |
7 | [Visit the live website here.](https://esri.github.io/building-viewer)
8 |
9 | 
10 |
11 |
12 | ## Features
13 | * Building exploration - discover the great Turanga library
14 | * Customisation - use this app to display your own building
15 | * Discover the building floor by floor on a 2D visualisation
16 | * Get a broader perspective of the building surroundings
17 |
18 | ## Instructions
19 |
20 | ### To get a live copy on your machine
21 |
22 | 1. Clone the repo and `npm install` dependencies
23 | 2. Remove ref to this repo: `rm -rf .git`
24 | 3. `npm run build` to compile `src/js/*.ts` and `src/css/*.sccs` files in the same folder and watch for changes
25 | 4. `npm run server` launches a webserver.
26 | 5. Open your browser and enter the local address `http://localhost:8888/`. You should see now the Building Viewer running.
27 |
28 | ### To add your own building
29 |
30 | 1. Create a webscene with a BuildingSceneLayer named `Building
31 | 2. Open `src/config.tsx` in your favorite code editor
32 | 3. Delete all the content except the two first obscure lines
33 | 4. Now you need to define 2 parameters in the config to get started:
34 |
35 | The `websceneId` of the webscene you created above
36 | ```
37 | export const websceneId = "YOUR WEBSCENE ID HERE";
38 | ```
39 | *Note that you may to also export on which portal this webscene resides if different from the ArcGis's portal: `export const portalUrl = "https://your-portal-url.com";`*
40 |
41 | The `sections` you'd like to have in your Building Viewer (see documentation about sections). Let's start with only one section, the home page:
42 | ```typescript
43 | // first import the section:
44 | import HomeSection = require("./sections/HomeSection");
45 |
46 | // then export the `sections` parameter:
47 | export const sections = [
48 | new HomeSection({})
49 | ];
50 | ```
51 |
52 | 5. Recompile the code and reload the website.
53 |
54 | Checkout the documentation in the `docs` folder, and in particular the [quick start guide](./docs/Quickstart.md).
55 |
56 | ## Requirements
57 |
58 | * Notepad or your favorite HTML editor
59 | * `npm` and some knowledge of [Typescript](https://www.typescriptlang.org/)
60 | * Web browser with access to the Internet
61 |
62 | ## Resources
63 |
64 | The following external libraries, APIs, open datasets and specifications were used to make this application:
65 |
66 | * [ArcGIS API for JavaScript](https://developers.arcgis.com/javascript/)
67 | * [Calcite Web](http://esri.github.io/calcite-web/)
68 | * Turangua's BIM data provided by [Christchurch City Council](https://www.ccc.govt.nz/)
69 | * [Christchurch city model](https://www.linz.govt.nz/news/2014-03/3d-models-released-christchurch-city) provided by [Christchurch City Council](https://www.ccc.govt.nz/)
70 | * [Roboto font](https://fonts.google.com/specimen/Roboto)
71 |
72 | ## Issues
73 |
74 | Find a bug or want to request a new feature? Please let us know by submitting an issue.
75 |
76 | ## Contributing
77 |
78 | Esri welcomes contributions from anyone and everyone. Please see our [guidelines for contributing](https://github.com/esri/contributing).
79 |
80 | ## Licensing
81 | Copyright 2019 Esri
82 |
83 | Licensed under the Apache License, Version 2.0 (the "License");
84 | you may not use this file except in compliance with the License.
85 | You may obtain a copy of the License at
86 |
87 | http://www.apache.org/licenses/LICENSE-2.0
88 |
89 | Unless required by applicable law or agreed to in writing, software
90 | distributed under the License is distributed on an "AS IS" BASIS,
91 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92 | See the License for the specific language governing permissions and
93 | limitations under the License.
94 |
95 | A copy of the license is available in the repository's [license.txt](license.txt) file.
96 |
--------------------------------------------------------------------------------
/building-viewer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Esri/building-viewer/4b677fffb43b566e0f001e8b9fd04115ad078b9d/building-viewer.png
--------------------------------------------------------------------------------
/docs/NameConventions.md:
--------------------------------------------------------------------------------
1 | # Naming conventions
2 |
3 | ## Layers
4 |
5 | Besides the required the building scene layer, without which the entire application would not have any sense, there are some layers and their names that have specific meaning. Any other layer you add to the scene will be left untouched and visible at all time in your application.
6 |
7 | All the names need to include the following, but you can have longer layer's name, e.g. "Building: Turanga Library".
8 |
9 | - `"Building"`: this is the only required layer in your webscene. It needs to be a [Building Scene Layer](https://developers.arcgis.com/javascript/latest/api-reference/)
10 | - `"City model"`: this is an optional layer in your webscene that show the surroundings. It needs to be a [Scene Layer](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-SceneLayer.html).
11 | - `"Floor points"`: this layer needs to be a [`FeatureLayer`](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html) that will be added to the `FloorsSection`. Additionally, if every features have a number attribute `BldgLevel`, the feature filtered depending on the selected floor (See [FloorsSection](./Sections.html)).
12 | - `"Floor pictures"`: this layer needs to be a [`FeatureLayer`](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html), displaying icons, with the string attributes `url`, `title` and `credit`. It will be added to the `FloorsSection`. When a user cliks on it, a popup is displayed with the information provided through the attributes. Additionally, if every features have a number attribute `BldgLevel`, the feature filtered depending on the selected floor (See [FloorsSection](./Sections.html)).
13 | - `"External pictures"`: this layer needs to be a [`FeatureLayer`](https://developers.arcgis.com/javascript/latest/api-reference/esri-layers-FeatureLayer.html), displaying icons, with the string attributes `url`, `title` and `credit`. It will be added to the `HomeSection`. When a user cliks on it, a popup is displayed with the information provided through the attributes.
14 |
15 |
16 | ### Prefix
17 |
18 | - `"Surroundings:"`: Any layer with name starting with this prefix will be added to the `SurroundingsSection` on the left. A toggle allows user to turn them on or off (off by default).
19 |
20 |
21 | ## Slides
22 |
23 | Slides with the following names have specific roles:
24 |
25 | - `"Overview"`: The slide's camera will be automatically be applied to the view when a user goes to the `HomeSection`
26 | - `"Floor by floor"`: The slide's camera will be automatically be applied to the view when a user goes to the `FloorsSection`
27 | - `"Surroundings"`: The slide's camera will be automatically be applied to the view when a user goes to the `SurroundingsSection`
28 | - In general, a section is looking among the slide's names and any slide matching the section's title will be applied to the view when a user goes to this section
29 |
30 | ### Prefix
31 |
32 | Additionally, this prefix has also a specific meaning:
33 |
34 | - `"Points of Interest:"`: this suffix is used by the `SurroundingsSection` to display different interactive point of interests. When a user clicks on any of the point of interests, the camera is applied to the view.
35 |
--------------------------------------------------------------------------------
/docs/Quickstart.md:
--------------------------------------------------------------------------------
1 | # Minimal setup
2 |
3 | ### Prepare a minimal webscene:
4 | 1. Create a new webscene
5 | 1. Add a Building Scene layer and add the suffix `Building:` in front of it's name
6 | 1. Save the webscene, give it a nice title and copy the webscene id. You will need it in a few next steps
7 |
8 | ### Get your own copy of the Building Viewer
9 |
10 | 1. Clone the git repository in your local machine and then go into the folder:
11 | ```
12 | git clone https://github.com/Esri/building-viewer
13 | cd BSL-Demo-App
14 | ```
15 | 1. Remove the `.git` repository reference `rm -rf .git`
16 | 1. Install dependency: `npm run install`
17 | 1. Compile the code: `npm run build`
18 | 1. Start the server: `npm run server`
19 | 1. Open your browser and enter the local address `http://localhost:8888/`. You should see now the Building Viewer running:
20 |
21 |
22 | 
23 |
24 |
25 | ### Edit the content with your own webscene
26 | 1. Open `src/config.tsx` in your favorite code editor
27 | 2. Delete all the content except the two first obscure lines
28 | 3. Now you need to define 2 parameters in the config to get started:
29 | 1. The `websceneId` of the webscene you created above
30 | ```
31 | export const websceneId = "YOUR WEBSCENE ID HERE";
32 | ```
33 | *Note that you may to also export on which portal this webscene resides if different from the ArcGis's portal: `export const portalUrl = "https://your-portal-url.com";`*
34 | 2. The `sections` you'd like to have in your Building Viewer (see documentation about sections). Let's start with only one section, the home page:
35 | ```typescript
36 | // first import the section:
37 | import HomeSection = require("./sections/HomeSection");
38 |
39 | // then export the `sections` parameter:
40 | export const sections = [
41 | new HomeSection({})
42 | ];
43 | ```
44 | 4. Recompile the code (note that the Building Viewer ships with a little util that watch files and recompile for you. Just run `npm run dev` in a differnet terminal window)
45 | 5. You should see your building in the Building Viewer now if you reload your webviewer:
46 |
47 | 
48 |
49 | ### Let's add content
50 |
51 | The title of the home page is the title of your scene. If you would like to change it, you can simply update the title of the scene.
52 |
53 | 1. First, the view of the building when opening the Building Viewer isn't really Building appealing. Let's change this.
54 | 1. Go back to your webscene
55 | 2. Navigate to a view that please you
56 | 3. Save a slide with name "Overview"
57 | 4. Save your webscene and reload the demo Building Viewer. You should have a better view when you enter the Building Viewer:
58 |
59 | 
60 |
61 | 2. Let's add some description on the left side:
62 | 1. Go back to your webscene and go to "Properties"
63 | 2. Add your text in the webscene description
64 | 3. Save the webscene and reload the demo Building Viewer. You should now see your text on the left side:
65 |
66 | 
67 |
68 | 3. Let's add different point of views for the user to appreciate the building in all corners:
69 | 1. Go back to your webscene and go to "Slides"
70 | 2. Move your view to the location you would like
71 | 3. Create a new slide
72 | 4. Repeat as much as you want, and then save your scene
73 | 5. Reload the demo app, you should see now some points of view:
74 |
75 | 
76 |
77 | 4. Let's add the opening hours:
78 | 1. Go now to the file `src/config.tsx`
79 | 2. Import the necessary classes:
80 | ```typescript
81 | import {Timetable, DayTimetable} from "./widgets/Timetable/Timetable";
82 | import Collection = require("esri/core/Collection");
83 | ```
84 | 3. Add the timetable to the home section, and add all your opening hours for every day:
85 | ```typescript
86 | new HomeSection({
87 | timetable: new Timetable({
88 | dates: new Collection([
89 | new DayTimetable({
90 | opens: "8:00",
91 | closes: "20:00"
92 | }),
93 | new DayTimetable({
94 | opens: "8:00",
95 | closes: "20:00"
96 | }),
97 | new DayTimetable({
98 | opens: "8:00",
99 | closes: "20:00"
100 | }),
101 | new DayTimetable({
102 | opens: "8:00",
103 | closes: "20:00"
104 | }),
105 | new DayTimetable({
106 | opens: "8:00",
107 | closes: "20:00"
108 | }),
109 | new DayTimetable({
110 | opens: "10:00",
111 | closes: "17:00"
112 | }),
113 | new DayTimetable({
114 | opens: "10:00",
115 | closes: "17:00"
116 | })
117 | ])
118 | })
119 | })
120 | ```
121 | 4. Save your file, recompile the typsecrip and reload the app
122 | 5. You should now see the time table on the bottom left of the home page:
123 |
124 | 
125 |
126 |
127 | 5. Change the ground colour
128 | 1. The design of the Building Viewer has been set to work well with darker colours. Let's change the background for a dark solid colour
129 | 2. Go back to your webscene and click on "Ground". Choose a good ground colour
130 | 3. Go to the basemap gallery, and check "No basemap".
131 | 4. Save your webscene *(as the initial state)* and reloads the Building Viewer:
132 |
133 | 
134 |
135 | 5. Finally, let's add city buildings:
136 | 1. If you have the city as Building Scene Layer, you can add it to your webscene and name it "City model".
137 | 2. Save your webscene and reload the Building Viewer, you should see now the surroundings building:
138 |
139 | 
140 |
141 | *Note that all others layers or configuration of your webscene will appear in your demo app.*
142 |
143 | ---
144 |
145 | To go beyond and add more section, please read [the sections documentation](./Sections.md). You can always check the [naming conventions](./NameConventions.md) for a quick look at the different layer and slide names.
146 |
--------------------------------------------------------------------------------
/docs/Sections.md:
--------------------------------------------------------------------------------
1 | # Sections
2 |
3 | The demo uses 3 different sections, the home section, the floor section and the surroundings section. In the following content, we will go through the initialisation of the different sections and explore how you can create new sections.
4 |
5 | ## General section parameters
6 |
7 | - Every section has a configurable `title` which is then the word appearing in the menu.
8 | - To configure the camera of any section, you can add a slide with the coresponding home's title as the name of the slides.
9 |
10 | ## Home section
11 |
12 | The home section ships with 3 main part:
13 |
14 | - The **description of the building** on the left. By default, the description is taken from the scene's description. As shown in the `Quickstart` guide, you can add your text in the `Properties` pane of the Scene Viewer. However, if you'd like to add more complex content, e.g. involving html or somejavacsript logic, you can always pass a parameter `content` to the `HomeSection` constructor. This parameter is a function that takes in argument the section and return some `VNodes` that will be later added to the left. You are now totally free to design the exact content you would like.
15 | - The **viewpoints**: every slides that you create in your webscene with automatically be added as a viewpoints on the right of the HomeSection if the slide's name is not part of the [reserved names](./NameConventions.md).
16 | - The **building opening hours**: you can pass to your Building Viewer a list of opening hours as follow:
17 | ```typescript
18 | new HomeSection({
19 | timetable: new Timetable({
20 | dates: new Collection([
21 | new DayTimetable({
22 | opens: "8:00",
23 | closes: "20:00"
24 | }),
25 | new DayTimetable({
26 | opens: "8:00",
27 | closes: "20:00"
28 | }),
29 | new DayTimetable({
30 | opens: "8:00",
31 | closes: "20:00"
32 | }),
33 | new DayTimetable({
34 | opens: "8:00",
35 | closes: "20:00"
36 | }),
37 | new DayTimetable({
38 | opens: "8:00",
39 | closes: "20:00"
40 | }),
41 | new DayTimetable({
42 | opens: "10:00",
43 | closes: "17:00"
44 | }),
45 | new DayTimetable({
46 | opens: "10:00",
47 | closes: "17:00"
48 | })
49 | ])
50 | })
51 | })
52 | ```
53 |
54 | ## Floor section
55 |
56 | The floor section will display a floor picker on the right that allows the user to discover the different level's of the building. There is two way to initialise the floor section. You can either pass the lowest floor number and the hightest floor number as follow:
57 |
58 | ```typescript
59 | new FloorsSection({
60 | minFloor: 0,
61 | maxFloor: 2
62 | })
63 | ```
64 |
65 | which will allow the user to go through floors 0 to 2. Or you can pass every floor with the content it needs to display on the left of the building when a user select one:
66 |
67 | ```typescript
68 | new FloorsSection({
69 | floors: new Collection([
70 | ...,
71 | new Floor({
72 | title: "The name of the floor",
73 | subtitle: "A subtitle",
74 | floor: 0,
75 | content: () => (
Some html content
)
76 | }),
77 | ...
78 | ])
79 | })
80 | ```
81 |
82 | This uses [TSX](https://www.typescriptlang.org/docs/handbook/jsx.html) to render the content. Be sure to include the `tsx` function [from the ArcGIS for Javascript API](https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-support-widget.html#tsx) to compile this code.
83 |
84 |
85 | ## Surroundings section
86 |
87 |
88 | The surroundings section display toggles for extra layers you can setup in your webscene or point of view for different building in your surrounding layer, using your webscene's slides (See [naming concention](./NamingConvention.md)).
89 |
90 | It does not take any parameter to be initialised:
91 |
92 | ```typescript
93 | new SurroundingsSection({})
94 | ```
95 |
96 | - To add toggle for extra layers, just add `"Surroundings:"` in front of their layer's name in your webscene.
97 | - To add toggle for point of view of building in your "surroundings" layer, just add slides with `"Points of Interest:"` in their title.
98 |
99 |
100 | ## Create your own section
101 |
102 | The building viewer as been designed so that you can easily extend it. Every section share a common base class `Section` which defines the minimal structure for it to be displayed. If you want to create your own section, you can simple extend this class and define the `title`, give it a unique `id` and define what goes on the right side by delcaring `, and what goes on the left by define `render`.
103 |
104 | As an example:
105 |
106 | ```
107 | @subclass()
108 | class MySection extends declared(Section) {
109 | @property()
110 | title = "My section"
111 |
112 | @property()
113 | id = "my-section"
114 |
115 | render() {
116 | return ();
117 | }
118 |
119 | paneRight() {
120 | return ();
121 | }
122 | ```
123 |
124 | You can of course create a complex widget here. This is following the ArcGIS for Javascript API's [widget convention and structure](https://developers.arcgis.com/javascript/latest/api-reference/esri-widgets-Widget.html). Be sure to check their [guide first](https://developers.arcgis.com/javascript/latest/guide/custom-widget/index.html).
125 |
--------------------------------------------------------------------------------
/docs/images/screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Esri/building-viewer/4b677fffb43b566e0f001e8b9fd04115ad078b9d/docs/images/screenshot_1.png
--------------------------------------------------------------------------------
/docs/images/screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Esri/building-viewer/4b677fffb43b566e0f001e8b9fd04115ad078b9d/docs/images/screenshot_2.png
--------------------------------------------------------------------------------
/docs/images/screenshot_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Esri/building-viewer/4b677fffb43b566e0f001e8b9fd04115ad078b9d/docs/images/screenshot_3.png
--------------------------------------------------------------------------------
/docs/images/screenshot_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Esri/building-viewer/4b677fffb43b566e0f001e8b9fd04115ad078b9d/docs/images/screenshot_4.png
--------------------------------------------------------------------------------
/docs/images/screenshot_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Esri/building-viewer/4b677fffb43b566e0f001e8b9fd04115ad078b9d/docs/images/screenshot_5.png
--------------------------------------------------------------------------------
/docs/images/screenshot_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Esri/building-viewer/4b677fffb43b566e0f001e8b9fd04115ad078b9d/docs/images/screenshot_6.png
--------------------------------------------------------------------------------
/docs/images/screenshot_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Esri/building-viewer/4b677fffb43b566e0f001e8b9fd04115ad078b9d/docs/images/screenshot_7.png
--------------------------------------------------------------------------------
/docs/images/screenshot_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Esri/building-viewer/4b677fffb43b566e0f001e8b9fd04115ad078b9d/docs/images/screenshot_8.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Building Viewer
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | Apache License - 2.0
2 |
3 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
4 |
5 | 1. Definitions.
6 |
7 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
8 |
9 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
10 |
11 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control
12 | with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management
13 | of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial
14 | ownership of such entity.
15 |
16 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
17 |
18 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source,
19 | and configuration files.
20 |
21 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to
22 | compiled object code, generated documentation, and conversions to other media types.
23 |
24 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice
25 | that is included in or attached to the work (an example is provided in the Appendix below).
26 |
27 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the
28 | editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes
29 | of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of,
30 | the Work and Derivative Works thereof.
31 |
32 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work
33 | or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual
34 | or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of
35 | electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on
36 | electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for
37 | the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing
38 | by the copyright owner as "Not a Contribution."
39 |
40 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and
41 | subsequently incorporated within the Work.
42 |
43 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual,
44 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display,
45 | publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
46 |
47 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide,
48 | non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell,
49 | sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are
50 | necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was
51 | submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work
52 | or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You
53 | under this License for that Work shall terminate as of the date such litigation is filed.
54 |
55 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications,
56 | and in Source or Object form, provided that You meet the following conditions:
57 |
58 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and
59 |
60 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and
61 |
62 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices
63 | from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
64 |
65 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a
66 | readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the
67 | Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the
68 | Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever
69 | such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License.
70 | You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work,
71 | provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to
72 | Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your
73 | modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with
74 | the conditions stated in this License.
75 |
76 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You
77 | to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above,
78 | nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
79 |
80 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except
81 | as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
82 |
83 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides
84 | its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation,
85 | any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for
86 | determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under
87 | this License.
88 |
89 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required
90 | by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages,
91 | including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the
92 | use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or
93 | any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
94 |
95 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a
96 | fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting
97 | such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree
98 | to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your
99 | accepting any such warranty or additional liability.
100 |
101 | END OF TERMS AND CONDITIONS
102 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "building-viewer",
3 | "version": "1.0.0",
4 | "description": "This application demonstrates the use of various Building Scene Layer related API",
5 | "repository": {
6 | "type": "git",
7 | "url": "git@github.com:esri/building-viewer.git"
8 | },
9 | "scripts": {
10 | "dev": "tsc -w & sass --watch src:src",
11 | "lint": "tslint './src/*.ts'",
12 | "build": "tsc & sass src:src",
13 | "dist": "grunt dist",
14 | "server": "grunt"
15 | },
16 | "keywords": [
17 | "esri",
18 | "typescript"
19 | ],
20 | "author": "Yannik Messerli ",
21 | "license": "Apache-2.0",
22 | "devDependencies": {
23 | "grunt": "^1.3.0",
24 | "grunt-contrib-concat": "*",
25 | "grunt-contrib-connect": "*",
26 | "grunt-contrib-copy": "*",
27 | "grunt-contrib-htmlmin": "*",
28 | "grunt-contrib-uglify": "*",
29 | "grunt-contrib-watch": "*",
30 | "grunt-dojo": "^1.1.2",
31 | "grunt-include-source": "^1.1.0",
32 | "grunt-run": "^0.8.1",
33 | "grunt-stripcomments": "^0.7.2",
34 | "js-yaml": ">=3.13.1",
35 | "tslint": "^4.2.0",
36 | "tslib": "~2.4.0",
37 | "sass": "^1.49.10"
38 | },
39 | "dependencies": {
40 | "@types/dojo": "1.9.41",
41 | "typescript": "4.2.4"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/css/main.scss:
--------------------------------------------------------------------------------
1 | @import "./variables.scss";
2 | @import "./mixins.scss";
3 |
4 | /***************
5 | Base
6 | ****************/
7 |
8 | html,
9 | body,
10 | #mainViewDiv, #appDiv, #popup {
11 | padding: 0;
12 | margin: 0;
13 | height: 100%;
14 | width: 100%;
15 | }
16 |
17 | #appDiv {
18 | top: 0;
19 | pointer-events: none;
20 | position: absolute;
21 | overflow: hidden;
22 | }
23 |
24 | #mainViewDiv {
25 | position: absolute;
26 | bottom: 0;
27 | left: 0;
28 | @include transition(all 0.8s);
29 | }
30 |
31 | /***************
32 | Typography
33 | ****************/
34 |
35 | #appDiv {
36 | color: $primaryColor;
37 | font-size: $generalFontSize;
38 | line-height: $generalFontSize + 7px;
39 | text-shadow: 1px 1px 5px rgba(70,70,70, 1);
40 |
41 | a {
42 | color: $primaryColor;
43 | text-decoration: underline;
44 | }
45 |
46 | h1, h2, h3, h4, h5 {
47 | // font-family: "HelveticaNeue-CondensedBlack", "Helvetica Neue";
48 | font-family: 'Roboto Condensed', sans-serif;
49 | text-transform: uppercase;
50 | font-stretch: "condensed";
51 | pointer-events: all;
52 | font-weight: 900;
53 | text-shadow: 0 0 5px rgba(70,70,70, 1);
54 |
55 | &.slash-title {
56 | text-transform: none;
57 | font-family: "Avenir Next";
58 | font-stretch: "normal";
59 | font-weight: normal;
60 |
61 | &::before {
62 | content: "/";
63 | margin-right: 10px;
64 | }
65 | }
66 |
67 | &.inline {
68 | margin-right: 10px;
69 | }
70 | }
71 |
72 | h2.slash-title {
73 | margin-bottom: 50px;
74 | margin-top: 30px;
75 | }
76 |
77 | h5.inline {
78 | font-family:'Roboto Condensed', sans-serif;
79 | font-size: 15px;
80 | }
81 |
82 | .inline {
83 | display: inline;
84 | }
85 |
86 | p {
87 | pointer-events: all;
88 | }
89 |
90 | h1 {
91 | // font-family: "HelveticaNeue-CondensedBlack", "Helvetica Neue";
92 | font-family:'Roboto Condensed', sans-serif;
93 | font-stretch: "condensed";
94 | font-weight: 900;
95 | font-size: $bigTitleFontSize;
96 | line-height: $bigTitleFontSize;
97 | margin-bottom: 30px;
98 | }
99 | }
100 |
101 | @media (max-width: 1400px) {
102 | #appDiv {
103 | font-size: 12px;
104 | line-height: 17px;
105 |
106 | .active .viewpoints {
107 | margin-top: 50px;
108 |
109 | h2.slash-title {
110 | margin-bottom: -5px;
111 | }
112 |
113 | .viewpoint {
114 | font-size: 21px;
115 | }
116 | }
117 | #surroundings h1 {
118 | font-size: 30px;
119 | margin-bottom: 10px;
120 | }
121 |
122 | #surroundings .slash-title.width-toggle {
123 | font-size: 20px;
124 | }
125 |
126 | #surroundings .content {
127 | font-size: 17px;
128 | }
129 |
130 | #surroundings .content {
131 | line-height: 30px;
132 | }
133 |
134 | #surroundings .content svg {
135 | width: 13px;
136 | vertical-align: -2px;
137 | }
138 |
139 | #surroundings .element a {
140 | margin-left: 8px;
141 | }
142 |
143 | #menu {
144 | font-size: 18px;
145 |
146 | .slash {
147 | margin-left: 20px;
148 | margin-right: 15px;
149 | }
150 | }
151 |
152 | .side-container {
153 | max-width: 290px;
154 | }
155 |
156 | h1 {
157 | font-size: 50px;
158 | line-height: 50px;
159 | }
160 |
161 | h2.slash-title {
162 | margin-top: 10px;
163 | margin-bottom: 10px;
164 | font-size: 25px;
165 | }
166 |
167 | .timetable {
168 | margin-top: 20px;
169 |
170 | .daytime {
171 | height: 20px;
172 |
173 | h2 {
174 | font-size: 20px;
175 | margin-top: -4px;
176 | }
177 |
178 | h3 {
179 | font-size: 15px;
180 | margin-bottom: 3px;
181 | }
182 | }
183 | }
184 |
185 | #surroundings .content {
186 | margin-top: 0px;
187 | }
188 |
189 | .active .floor-selector .level:first-child {
190 | margin-top: 0;
191 | }
192 |
193 | #floors {
194 | h1 {
195 | font-size: 40px;
196 | line-height: 40px;
197 | margin-left: 70px;
198 | width: 220px;
199 |
200 | &.number {
201 | font-size: 100px;
202 | margin-top: 20px;
203 | margin-left: 0;
204 | }
205 | }
206 |
207 | h3.subtitle {
208 | font-size: 25px;
209 | margin-left: 75px;
210 | letter-spacing: 4px;
211 | }
212 |
213 | .level {
214 | margin-top: 33px;
215 | margin-left: 12px;
216 | }
217 | }
218 |
219 | .side-container.left .pane {
220 | width: 290px;
221 | }
222 | }
223 | }
224 |
225 | /***************
226 | Layout
227 | ****************/
228 |
229 | .side-container {
230 | top: 0;
231 | position: absolute;
232 | max-width: 390px;
233 | margin: 50px;
234 | }
235 | .side-container.left {
236 | left: 0;
237 | }
238 |
239 | .side-container.right {
240 | right: 0;
241 | margin-top: 20px;
242 | text-align: right;
243 | }
244 |
245 | #menu {
246 | pointer-events: all;
247 | text-align: center;
248 | position: absolute;
249 | left: 0;
250 | position: absolute;
251 | margin-left: auto;
252 | margin-right: auto;
253 | width: 100%;
254 | text-align: center;
255 | display: block;
256 | font-size: $menuItemFontSize;
257 | font-family: "Avenir Next";
258 | font-stretch: "normal";
259 | font-weight: medium;
260 | top: $menuTopPosition;
261 | cursor: pointer;
262 |
263 | .slash {
264 | margin-left: $menuSlashMarginLeft;
265 | margin-right: $menuSlashMarginRight;
266 | }
267 |
268 | a {
269 | text-decoration: none;
270 | color: $menuItemColor;
271 | cursor: pointer;
272 | @include transition(font-size 0.3s);
273 |
274 | &.active {
275 | font-size: $menuItemActiveFontSize;
276 | color: $menuItemActiveColor;
277 |
278 | &:hover {
279 | color: $menuItemActiveColor;
280 | }
281 | }
282 |
283 | &:hover {
284 | font-size: $menuItemOverFontSize;
285 | color: $menuItemOverColor;
286 | }
287 | }
288 |
289 | }
290 |
291 | .esri-attribution {
292 | display: none;
293 | }
294 |
--------------------------------------------------------------------------------
/src/css/mixins.scss:
--------------------------------------------------------------------------------
1 | /***************
2 | Mixins
3 | ****************/
4 |
5 | @mixin transition($transition...) {
6 | -moz-transition: $transition;
7 | -o-transition: $transition;
8 | -webkit-transition: $transition;
9 | transition: $transition;
10 | }
11 |
12 | @mixin transition-property($property...) {
13 | -moz-transition-property: $property;
14 | -o-transition-property: $property;
15 | -webkit-transition-property: $property;
16 | transition-property: $property;
17 | }
18 | @mixin transition-duration($duration...) {
19 | -moz-transition-property: $duration;
20 | -o-transition-property: $duration;
21 | -webkit-transition-property: $duration;
22 | transition-property: $duration;
23 | }
24 | @mixin transition-timing-function($timing...) {
25 | -moz-transition-timing-function: $timing;
26 | -o-transition-timing-function: $timing;
27 | -webkit-transition-timing-function: $timing;
28 | transition-timing-function: $timing;
29 | }
30 | @mixin transition-delay($delay...) {
31 | -moz-transition-delay: $delay;
32 | -o-transition-delay: $delay;
33 | -webkit-transition-delay: $delay;
34 | transition-delay: $delay;
35 | }
36 |
--------------------------------------------------------------------------------
/src/css/variables.scss:
--------------------------------------------------------------------------------
1 | /***************
2 | Colors
3 | ****************/
4 |
5 | // Global:
6 | $primaryColor: #fff;
7 | $secondaryColor: #a3a3a3;
8 | $orange: #F6A803;
9 |
10 | // Menu:
11 | $menuItemColor: $primaryColor;
12 | $menuItemActiveColor: $orange;
13 | $menuItemOverColor: $primaryColor;
14 |
15 | // Viewpoints:
16 | $viewpointItemColor: $primaryColor;
17 | $viewpointItemActiveColor: $orange;
18 | $viewpointItemOverColor: $primaryColor;
19 |
20 | // Floors
21 |
22 | // Surroundings
23 |
24 |
25 | /***************
26 | Font sizing
27 | ****************/
28 |
29 | $generalFontSize: 15px;
30 | $bigTitleFontSize: 5 * $generalFontSize + 10px;
31 |
32 | // Menu
33 | $menuItemFontSize: 23px;
34 | $menuItemActiveFontSize: 23px;
35 | $menuItemOverFontSize: 26px;
36 |
37 | // Viewpoints
38 | $viewpointItemFontSize: 25px;
39 | $viewpointItemActiveFontSize: 40px;
40 | $viewpointItemOverFontSize: 40px;
41 |
42 | // Floors
43 | $floorSelectorLevelFontSize: 25px;
44 | $floorSelectorLevelOverFontSize: 60px;
45 | $floorSelectorLevelActiveFontSize: 90px;
46 |
47 | /***************
48 | Sizing
49 | ****************/
50 |
51 | // Menu
52 | $menuTopPosition: 25px;
53 | $menuSlashMarginLeft: $menuItemFontSize + 2px;
54 | $menuSlashMarginRight: $menuItemFontSize - 3px;
55 |
--------------------------------------------------------------------------------
/src/js/AppState.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Esri
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | import { subclass, property } from "esri/core/accessorSupport/decorators";
18 | import Accessor from "esri/core/Accessor";
19 | import SceneView from "esri/views/SceneView";
20 | import BuildingVisualisation from "./support/BuildingVisualisation";
21 | import PopupInfo from "./widgets/Popup/PopupInfo";
22 |
23 | @subclass("AppState")
24 | class AppState extends Accessor {
25 | @property()
26 | pageLocation: string;
27 |
28 | @property()
29 | floorNumber = 0;
30 |
31 | @property()
32 | view: SceneView;
33 |
34 | @property()
35 | buildingLayer: BuildingVisualisation;
36 |
37 | @property()
38 | popupInfo: PopupInfo;
39 | }
40 |
41 | export = AppState;
42 |
--------------------------------------------------------------------------------
/src/js/BuildingViewer.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Esri
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | import { subclass, property } from "esri/core/accessorSupport/decorators";
18 | import { tsx } from "esri/widgets/support/widget";
19 |
20 | // esri
21 | import Sections from "./sections/Sections";
22 | import SceneView from "esri/views/SceneView";
23 | import Widget from "esri/widgets/Widget";
24 | import * as promiseUtils from "esri/core/promiseUtils";
25 | import Camera from "esri/Camera";
26 | import SceneLayer from "esri/layers/SceneLayer";
27 | import BuildingSceneLayer from "esri/layers/BuildingSceneLayer";
28 | import WebScene from "esri/WebScene";
29 |
30 | // BuildingViewer
31 | import Section from "./sections/Section";
32 | import BuildingVisualisation from "./support/BuildingVisualisation";
33 | import SurroundingsVisualisation from "./support/SurroundingsVisualisation";
34 | import AppState from "./AppState";
35 | import * as appUtils from "./support/appUtils";
36 | import Popup from "./widgets/Popup/Popup";
37 |
38 | type SectionSublcass = Pick;
39 |
40 | interface BuildingViewerCtorArgs {
41 | sections: Pick[];
42 | mapContainer: string;
43 | websceneId: string;
44 | portalUrl?: string;
45 | floorMapping?: (originalFloor: number) => number;
46 | extraQuery?: string;
47 | }
48 |
49 | @subclass("webSceneViewer.widgets.LayersLoading.LayersLoadingProgressBar")
50 | class BuildingViewer extends Widget {
51 | //--------------------------------------------------------------------------
52 | //
53 | // Properties
54 | //
55 | //--------------------------------------------------------------------------
56 |
57 | @property({ aliasOf: "appState.view"})
58 | view: SceneView;
59 |
60 | @property({ aliasOf: "sections.activeSection"})
61 | activeSection: SectionSublcass | string | number;
62 |
63 | @property()
64 | sections: Sections;
65 |
66 | @property()
67 | appState = new AppState();
68 |
69 | @property()
70 | websceneId: string;
71 |
72 | @property()
73 | extraQuery: string;
74 |
75 | @property()
76 | portalUrl: string;
77 |
78 | //--------------------------------------------------------------------------
79 | //
80 | // Variables:
81 | //
82 | //--------------------------------------------------------------------------
83 |
84 | @property({ aliasOf: "appState.buildingLayer"})
85 | buildingLayer: BuildingVisualisation;
86 |
87 | @property({ aliasOf: "appState.surroundingsLayer"})
88 | surroundingsLayer: SurroundingsVisualisation;
89 |
90 | private firstRendering: boolean = true;
91 |
92 | private rawSections: Pick[];
93 |
94 | //--------------------------------------------------------------------------
95 | //
96 | // Life circle
97 | //
98 | //--------------------------------------------------------------------------
99 |
100 | constructor(args: BuildingViewerCtorArgs) {
101 | super(args as any);
102 |
103 | this.view = appUtils.createViewFromWebScene({websceneId: args.websceneId, mapContainer: args.mapContainer, portalUrl: args.portalUrl});
104 |
105 | if (args.floorMapping) {
106 | this.floorMapping = args.floorMapping.bind(this);
107 | }
108 | }
109 |
110 | normalizeCtorArgs(args: BuildingViewerCtorArgs) {
111 | this.rawSections = args.sections;
112 | delete args["sections"];
113 |
114 | return args;
115 | }
116 |
117 | initialize() {
118 | this.sections = new Sections(this.rawSections, this.appState);
119 |
120 | (this.view.map as WebScene).when(() => {
121 | // Save the initial layers:
122 | promiseUtils
123 | .eachAlways(this.view.map.layers.map((l) => this.appState.view.whenLayerView(l)))
124 | .then(() => {
125 |
126 | ///////////////////////////////////
127 | // Main building to present:
128 | const BSL = this.appState.view.map.layers.find(layer => layer.title.indexOf(appUtils.MAIN_LAYER_PREFIX) > -1);
129 |
130 | if (!BSL) {
131 | throw new Error("Cannot find the main BuildingSceneLayer (" + appUtils.MAIN_LAYER_PREFIX + ") in the webscene " + this.websceneId);
132 | }
133 |
134 | const visualisationArgs: any = {
135 | appState: this.appState,
136 | layer: BSL as BuildingSceneLayer
137 | };
138 |
139 | if (this.floorMapping) {
140 | visualisationArgs.floorMapping = this.floorMapping;
141 | }
142 |
143 | if (this.extraQuery) {
144 | visualisationArgs.extraQuery = this.extraQuery;
145 | }
146 |
147 | this.buildingLayer = new BuildingVisualisation(visualisationArgs);
148 |
149 | ///////////////////////////////////
150 | // Optional surrounding's layer:
151 | const surroundingsLayer = this.appState.view.map.layers.find(layer => layer.title.toLowerCase().indexOf(appUtils.CITY_LAYER_PREFIX.toLowerCase()) > -1) as SceneLayer;
152 | if (surroundingsLayer) {
153 | this.surroundingsLayer = new SurroundingsVisualisation({
154 | layer: surroundingsLayer,
155 | appState: this.appState
156 | });
157 | }
158 | });
159 |
160 | ///////////////////////////////////
161 | // Setup camera:
162 | this.sections.forEach((section) => {
163 | const slide = (this.view.map as WebScene).presentation.slides.find((slide) => slide.title.text === section.title);
164 | if (slide) {
165 | section.camera = slide.viewpoint.camera;
166 | (this.view.map as WebScene).presentation.slides.remove(slide);
167 | }
168 | else {
169 | console.error("Could not find a slide for section " + section.title);
170 | }
171 | });
172 | });
173 |
174 | this.view.when(() => {
175 | // Debug:
176 | window["view"] = this.view;
177 | window["appState"] = this.appState;
178 |
179 | // Active first section:
180 | if (this.sections.length > 0) {
181 | this.sections.activateSection(this.sections.getItemAt(0).id);
182 | }
183 | });
184 |
185 | this.watch("activeSection", (activeSection) => {
186 | this.firstRendering = true;
187 | this.renderNow();
188 |
189 | setTimeout(() => {
190 | this.firstRendering = false;
191 | this.renderNow();
192 | }, 10)
193 | });
194 | }
195 |
196 | render() {
197 | return (
198 |
{this.sections.paneLeft(this.firstRendering)}
199 |
{this.sections.menu()}
200 |
{this.sections.paneRight(this.firstRendering)}
201 |
);
202 | }
203 |
204 | postInitialize() {
205 |
206 | this.own(this.sections.on("go-to", (camera: Camera) => {
207 | this.view.goTo(camera);
208 | }));
209 |
210 | new Popup({ appState: this.appState, container: "popup"});
211 | }
212 |
213 | floorMapping(num: number) { return num; }
214 | }
215 |
216 | export = BuildingViewer;
217 |
--------------------------------------------------------------------------------
/src/js/config.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Esri
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | import { tsx } from "esri/widgets/support/widget";
18 | import HomeSection from "./sections/HomeSection";
19 | import { FloorsSection, Floor } from "./sections/FloorsSection";
20 | import SurroundingsSection from "./sections/SurroundingsSection";
21 | import Collection from "esri/core/Collection";
22 | import {Timetable, DayTimetable} from "./widgets/Timetable/Timetable";
23 |
24 | export const portalUrl = "https://zurich.maps.arcgis.com";
25 |
26 | export const websceneId = "543648a92446497db8a92c06ce1ad0b1";
27 |
28 | export const sections = [
29 | // Check the different files
30 | // to adapt to your need
31 | // or create a new section by
32 | // implement a subclass from `Section`
33 |
34 | // The about Turangua section:
35 | new HomeSection({
36 | content: (that: any) => (
Tūranga is a library in Central Christchurch and the main library of Christchurch City Libraries, New Zealand. It is the largest library in the South Island and the third-biggest in New Zealand. The previous Christchurch Central Library opened in 1982 on the corner of Oxford Terrace and Gloucester Street but was closed after the February 2011 Christchurch earthquake and demolished in 2014 to make way for the Convention Centre Precinct.
Open an hour earlier than the rest of the building on weekdays, He Hononga | Connection, Ground Level is the place to return library items, collect holds, browse magazines, DVDs and new arrivals, visit the café or interact with the Discovery Wall.
It offers experiences geared towards a wide cross-section of our community. Grab a hot drink at the espresso bar, attend an event in our community arena, or help the kids explore the play and craft areas and children’s resources. It’s also a great place for young adults to hang out, play videogames, try out VR or get some study done.
Find resources and services to help you develop your knowledge about your own identity, your ancestors, your whakapapa and also about the place that they called home – its land and buildings.
Explore the nonfiction collection with thousands of books on a huge range of subjects. Get help with print and online resources for research or recreation. Use the public internet computers or, for those who want a low-key space to read or study, there is a separate room called ‘The Quiet Place’. Study, research or browse for some recreational reading.
Browse the World Languages, Music and Fiction collections, including Biographies and Graphic Novels. Visit the two roof gardens with great views across the city. Explore your creativity in the Production Studio using creative technology such as 3D printers and sewing machines. Create and edit music and video using the Audio/Video Studio, or take a class in the Computer Labs with a great range of software available.
)
107 | })
108 | ])
109 | }),
110 | // Surroundings:
111 | new SurroundingsSection({})
112 | ];
113 |
114 | export const floorMapping = (originalFloor: number) => {
115 | let floor = originalFloor + 1;
116 | if (floor >= 3) {
117 | floor += 1;
118 | }
119 |
120 | return floor;
121 | }
122 |
123 | export const extraQuery = " AND (Category <> 'Generic Models' OR OBJECTID_1 = 2) AND Category <> 'Walls' AND Category <> 'Roofs' AND Category <> 'Curtain Wall Mullions' AND Category <> 'Curtain Panels'";
124 |
--------------------------------------------------------------------------------
/src/js/sections/FloorsSection.tsx:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2019 Esri
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | */
17 | import { subclass, property } from "esri/core/accessorSupport/decorators";
18 | import { tsx } from "esri/widgets/support/widget";
19 | import Section from "./Section";
20 | import Collection from "esri/core/Collection";
21 | import Widget from "esri/widgets/Widget";
22 | import FloorSelector from "../widgets/FloorSelector/FloorSelector";
23 | import * as watchUtils from "esri/core/watchUtils";
24 | import FeatureLayer from "esri/layers/FeatureLayer";
25 | import Legend from "esri/widgets/Legend";
26 | import PopupInfo from "../widgets/Popup/PopupInfo";
27 | import * as appUtils from "../support/appUtils";
28 | import Handles from "esri/core/Handles";
29 | import AppState from "../AppState";
30 |
31 | @subclass("legendWrapper")
32 | class LegendWrapper extends Widget {
33 | @property()
34 | hide: boolean = true;
35 |
36 | @property({ constructOnly: true })
37 | appState: AppState;
38 |
39 | @property()
40 | legend: Legend;
41 |
42 | constructor(args: { appState: AppState }, container: string) {
43 | super(args as any);
44 | }
45 |
46 | postInitialize() {
47 | this.legend = new Legend({
48 | view: this.appState.view,
49 | layerInfos: []
50 | });
51 | }
52 |
53 | render() {
54 | return (