@Override
    public void renderTo(Target<?> target) {

      float width = parseWidth(node.getTags(), 1.5f);

      /* determine material */

      Material material = null;

      // TODO parse color

      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("material"));
      }

      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("surface"), Materials.WOOD);
      }

      /* calculate vectors and corners */

      double directionAngle = parseDirection(node.getTags(), PI);

      VectorXZ faceVector = VectorXZ.fromAngle(directionAngle);
      VectorXZ boardVector = faceVector.rightNormal();

      List<VectorXZ> cornerOffsets = new ArrayList<VectorXZ>(4);
      cornerOffsets.add(faceVector.mult(+0.25).add(boardVector.mult(+width / 2)));
      cornerOffsets.add(faceVector.mult(+0.25).add(boardVector.mult(-width / 2)));
      cornerOffsets.add(faceVector.mult(-0.25).add(boardVector.mult(+width / 2)));
      cornerOffsets.add(faceVector.mult(-0.25).add(boardVector.mult(-width / 2)));

      /* draw seat and backrest */

      target.drawBox(material, getBase().addY(0.5), faceVector, 0.05, width, 0.5);

      if (!node.getTags().contains("backrest", "no")) {

        target.drawBox(
            material,
            getBase().add(faceVector.mult(-0.23)).addY(0.5),
            faceVector,
            0.5,
            width,
            0.04);
      }

      /* draw poles */

      for (VectorXZ cornerOffset : cornerOffsets) {
        VectorXZ polePos = node.getPos().add(cornerOffset.mult(0.8));
        target.drawBox(material, polePos.xyz(getBase().y), faceVector, 0.5, 0.08, 0.08);
      }
    }
    @Override
    public void renderTo(Target<?> target) {

      float height = parseHeight(node.getTags(), 0.5f);
      float width = parseWidth(node.getTags(), 1);
      float depth = width / 2f;

      /* determine material */

      Material material = null;

      // TODO parse color

      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("material"));
      }

      if (material == null) {
        material =
            Materials.getSurfaceMaterial(
                node.getTags().getValue("surface"), Materials.GRITBIN_DEFAULT);
      }

      double directionAngle = parseDirection(node.getTags(), PI);

      VectorXZ faceVector = VectorXZ.fromAngle(directionAngle);
      VectorXZ boardVector = faceVector.rightNormal();

      /* draw box */
      target.drawBox(material, getBase(), faceVector, height, width, depth);

      /* draw lid */
      List<VectorXYZ> vs = new ArrayList<VectorXYZ>();
      vs.add(getBase().addY(height + 0.2));
      vs.add(
          getBase().add(boardVector.mult(width / 2)).add(faceVector.mult(depth / 2)).addY(height));
      vs.add(
          getBase().add(boardVector.mult(-width / 2)).add(faceVector.mult(depth / 2)).addY(height));
      vs.add(
          getBase()
              .add(boardVector.mult(-width / 2))
              .add(faceVector.mult(-depth / 2))
              .addY(height));
      vs.add(
          getBase().add(boardVector.mult(width / 2)).add(faceVector.mult(-depth / 2)).addY(height));
      vs.add(
          getBase().add(boardVector.mult(width / 2)).add(faceVector.mult(depth / 2)).addY(height));

      target.drawTriangleFan(material.brighter(), vs, null);
    }
    @Override
    public void renderTo(Target<?> target) {

      float lampHeight = 0.8f;
      float lampHalfWidth = 0.4f;
      float poleHeight = parseHeight(node.getTags(), 5f) - lampHeight;

      /* determine material */

      Material material = null;

      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("material"));
      }

      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("surface"), STEEL);
      }

      /* draw pole */
      target.drawColumn(material, null, getBase(), 0.5, 0.16, 0.08, false, false);
      target.drawColumn(material, null, getBase().addY(0.5), poleHeight, 0.08, 0.08, false, false);

      /* draw lamp */

      // lower part
      List<VectorXYZ> vs = new ArrayList<VectorXYZ>();
      vs.add(getBase().addY(poleHeight));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(lampHalfWidth, 0, lampHalfWidth));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(lampHalfWidth, 0, -lampHalfWidth));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(-lampHalfWidth, 0, -lampHalfWidth));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(-lampHalfWidth, 0, lampHalfWidth));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(lampHalfWidth, 0, lampHalfWidth));

      target.drawTriangleFan(material, vs, null);

      // upper part
      vs.clear();
      vs.add(getBase().addY(poleHeight + lampHeight));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(lampHalfWidth, 0, lampHalfWidth));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(-lampHalfWidth, 0, lampHalfWidth));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(-lampHalfWidth, 0, -lampHalfWidth));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(lampHalfWidth, 0, -lampHalfWidth));
      vs.add(getBase().addY(poleHeight + lampHeight * 0.8).add(lampHalfWidth, 0, lampHalfWidth));

      target.drawTriangleFan(material, vs, null);
    }
    @Override
    public void renderTo(Target<?> target) {

      boolean summit =
          node.getTags().containsKey("summit:cross") || node.getTags().contains("natural", "peak");

      float height = parseHeight(node.getTags(), summit ? 4f : 2f);
      float width = parseHeight(node.getTags(), height * 2 / 3);

      double thickness = min(height, width) / 8;

      /* determine material and direction */

      Material material = null;

      // TODO parse color

      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("material"));
      }

      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("surface"), Materials.WOOD);
      }

      double directionAngle = parseDirection(node.getTags(), PI);
      VectorXZ faceVector = VectorXZ.fromAngle(directionAngle);

      /* draw cross */

      target.drawBox(material, getBase(), faceVector, height, thickness, thickness);

      target.drawBox(
          material,
          getBase().addY(height - width / 2 - thickness / 2),
          faceVector,
          thickness,
          width,
          thickness);
    }
    @Override
    public void renderTo(Target<?> target) {

      /* determine material */

      Material material = null;

      // TODO parse color

      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("material"));
      }

      if (material == null) {
        material = Materials.getSurfaceMaterial(node.getTags().getValue("surface"), STEEL);
      }

      /* draw pole */
      target.drawColumn(material, null, getBase(), 1.2, 0.06, 0.06, false, true);

      /* draw basket */
      target.drawColumn(
          material, null, getBase().addY(0.5).add(0.25, 0f, 0f), 0.5, 0.2, 0.2, true, true);
    }