A program to simulate the Countdown Pedestrian Signals (CPS)

Countdown pedestrian signals (CPS) model is defined in Section 4E.02 of the 2009 Edition of the U.S. FHWA Manual on Uniform Traffic Control Devices with four indications (in fact only three can be used) presented in the following sequence:

  1. A steady WALKING PERSON (symbolizing WALK) signal indication means that a pedestrian facing the signal indication is permitted to start to cross the roadway in the direction of the signal indication, possibly in conflict with turning vehicles. The pedestrian shall yield the right-of-way to vehicles lawfully within the intersection at the time that the WALKING PERSON (symbolizing WALK) signal indication is first shown.
  2. A flashing UPRAISED HAND (symbolizing DONT WALK) signal indication means that a pedestrian shall not start to cross the roadway in the direction of the signal indication, but that any pedestrian who has already started to cross on a steady WALKING PERSON (symbolizing WALK) signal indication shall proceed to the far side of the traveled way of the street or highway, unless otherwise directed by a traffic control device to proceed only to the median of a divided highway or only to some other island or pedestrian refuge area.
  3. A steady UPRAISED HAND (symbolizing DONT WALK) signal indication means that a pedestrian shall not enter the roadway in the direction of the signal indication.
  4. A flashing WALKING PERSON (symbolizing WALK) signal indication has no meaning and shall not be used.

Thumbnail image of Figure 4E-1

This simulation program (output shown belows) simplifies the model into two main indications sequence (WALK and DON’T WALK). It demonstrates the techniques of rotation of image views and the implemtation of count down LED clock class in JavaFX. It can easily be changed to include the additional transition indication into the full three indications sequence in the official model.

Output of Pedestrian Signal Light Simulation with Countdown Clock

Output of Pedestrian Signal Light Simulation with Countdown Clock

The program takes the system clock and slices it into milli seconds for the count down pulses. The model also divides the ONE minute into two 30 seconds intervals: one 30 seconds for WALK and another 30 seconds for DONT WALK. It can be further enhanced with the pulse sounds if you like.

(Acknowledgement: Part of code of the clock class originates from Oracle’s digital clock sample).

PedestrianSignal.java

/**
 * Purpose: A program to simulate the pedestrian signal light
 *    with a thirty second count-down LED clock.
 * Resources: walk.jpg and dont_walk.jpg (you can download from anywhere on web)
 *
 * Author:  https://henry416.wordpress.com
 *
 * Compile: javac -cp "c:\progra~1\oracle\javafx runtime 2.0\lib\jfxrt.jar" PedestriannSignal.java
 * Execute: java -cp "c:\progra~1\oracle\javafx runtime 2.0\lib\jfxrt.jar";. PedestriannSignal
 */
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.effect.Effect;
import javafx.scene.effect.Glow;
import javafx.scene.effect.InnerShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Shear;
import javafx.util.Duration;
import java.util.Calendar;
 public class PedestrianSignal extends Application {
    private Clock clock;
    private Calendar calendar = Calendar.getInstance();
    private Image img_dont_walk = new Image("dont_walk.jpg");
    private Image img_walk = new Image("walk.jpg");
    private ImageView iv = new ImageView();

     @Override public void start(Stage stage) {
         calendar.setTimeInMillis(System.currentTimeMillis());
         int seconds = 60-calendar.get(Calendar.SECOND);
         Group root = new Group();
         Scene scene = new Scene(root);
         scene.setFill(Color.BLACK);
         VBox box = new VBox();

         // load the image
            if (seconds>30)
              { iv.setImage(img_dont_walk);}
            else
              { iv.setImage(img_walk);}

         // resizes the image to have width of 120 while preserving the ratio and using
         // higher quality filtering method; this ImageView is also cached to
         // improve performance
         iv.setImage(img_walk);
         iv.setFitWidth(120);
         iv.setPreserveRatio(true);
         iv.setSmooth(true);
         iv.setCache(true);

         box.getChildren().add(iv);

         // add digital clock
         clock = new Clock(Color.ORANGERED, Color.rgb(50,50,50));
         clock.setLayoutX(45);
         clock.setLayoutY(186);
         clock.getTransforms().add(new Scale(0.83f, 0.83f, 0, 0));
         // add clock to bo
         box.getChildren().add(clock);
         root.getChildren().add(box);

         stage.setTitle("Pedestrian Test");
         stage.setWidth(120);
         stage.setHeight(300);
         stage.setScene(scene);
         stage.sizeToScene();
         stage.show();
     }
    public void play() {
        clock.play();
    }
    @Override public void stop() {
        clock.stop();
    }
    /**
     * Clock made of 6 of the Digit classes for hours, minutes and seconds.
     */
    public class Clock extends Parent {
        private Digit[] digits;
        private Timeline delayTimeline, secondTimeline;
        public Clock(Color onColor, Color offColor) {
            // create effect for on LEDs
            Glow onEffect = new Glow(1.7f);
            onEffect.setInput(new InnerShadow());
            // create effect for on dot LEDs
            Glow onDotEffect = new Glow(1.7f);
            onDotEffect.setInput(new InnerShadow(5,Color.BLACK));
            // create effect for off LEDs
            InnerShadow offEffect = new InnerShadow();
            // create digits
            digits = new Digit[2];
            for (int i = 0; i < 2; i++) {
                Digit digit = new Digit(onColor, offColor, onEffect, offEffect);
                digit.setLayoutX(i * 80 + ((i + 1) % 2) * 20);
                digits[i] = digit;
                getChildren().add(digit);
            }
            // update digits to current time and start timer to update every second
            refreshClocks();
            play();
        }
        private void refreshClocks() {
            calendar.setTimeInMillis(System.currentTimeMillis());
            int seconds = 60-calendar.get(Calendar.SECOND);
            // load the image
            if (seconds>30)
              { iv.setImage(img_dont_walk);}
            else
              { iv.setImage(img_walk);}

            if (seconds >30)
              { seconds = seconds -30; }
            digits[0].showNumber(seconds / 10);
            digits[1].showNumber(seconds % 10);
        }
        public void play() {
            // wait till start of next second then start a timeline to call refreshClocks() every second
            delayTimeline = new Timeline();
            delayTimeline.getKeyFrames().add(
                    new KeyFrame(new Duration(1000 - (System.currentTimeMillis() % 1000)), new EventHandler<ActionEvent>() {
                        @Override public void handle(ActionEvent event) {
                            if (secondTimeline != null) {
                                secondTimeline.stop();
                            }
                            secondTimeline = new Timeline();
                            secondTimeline.setCycleCount(Timeline.INDEFINITE);
                            secondTimeline.getKeyFrames().add(
                                    new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
                                        @Override public void handle(ActionEvent event) {
                                            refreshClocks();
                                        }
                                    }));
                            secondTimeline.play();
                        }
                    })
            );
            delayTimeline.play();
        }
        public void stop(){
            delayTimeline.stop();
            if (secondTimeline != null) {
                secondTimeline.stop();
            }
        }
    }
    /**
     * Simple 7 segment LED style digit. It supports the numbers 0 through 9.
     */
    public static class Digit extends Parent {
        private static final boolean[][] DIGIT_COMBINATIONS = new boolean[][]{
                new boolean[]{true, false, true, true, true, true, true},
                new boolean[]{false, false, false, false, true, false, true},
                new boolean[]{true, true, true, false, true, true, false},
                new boolean[]{true, true, true, false, true, false, true},
                new boolean[]{false, true, false, true, true, false, true},
                new boolean[]{true, true, true, true, false, false, true},
                new boolean[]{true, true, true, true, false, true, true},
                new boolean[]{true, false, false, false, true, false, true},
                new boolean[]{true, true, true, true, true, true, true},
                new boolean[]{true, true, true, true, true, false, true}};
        private final Polygon[] polygons = new Polygon[]{
                new Polygon(2, 0, 52, 0, 42, 10, 12, 10),
                new Polygon(12, 49, 42, 49, 52, 54, 42, 59, 12f, 59f, 2f, 54f),
                new Polygon(12, 98, 42, 98, 52, 108, 2, 108),
                new Polygon(0, 2, 10, 12, 10, 47, 0, 52),
                new Polygon(44, 12, 54, 2, 54, 52, 44, 47),
                new Polygon(0, 56, 10, 61, 10, 96, 0, 106),
                new Polygon(44, 61, 54, 56, 54, 106, 44, 96)};
        private final Color onColor;
        private final Color offColor;
        private final Effect onEffect;
        private final Effect offEffect;
        public Digit(Color onColor, Color offColor, Effect onEffect, Effect offEffect) {
            this.onColor = onColor;
            this.offColor = offColor;
            this.onEffect = onEffect;
            this.offEffect = offEffect;
            getChildren().addAll(polygons);
            getTransforms().add(new Shear(-0.1,0));
            showNumber(0);
        }
        public void showNumber(Integer num) {
            if (num < 0 || num > 9) num = 0; // default to 0 for non-valid numbers
            for (int i = 0; i < 7; i++) {
                polygons[i].setFill(DIGIT_COMBINATIONS[num][i] ? onColor : offColor);
                polygons[i].setEffect(DIGIT_COMBINATIONS[num][i] ? onEffect : offEffect);
            }
        }
    }
     public static void main(String[] args) {
         Application.launch(args);
     }
 }

About henry416
I am a computer technology explorer and an university student based on Toronto. If you have any question, please feel free to discuss and comment here

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s